VSCode Language Server Protocol client implementation for extension integration with language servers
—
Utility classes and functions for error handling, configuration monitoring, and type conversion.
Utilities for monitoring VS Code settings and automatically managing client lifecycle.
/**
* Monitors VS Code configuration changes and manages language client lifecycle
* Automatically starts/stops the client based on configuration changes
*/
class SettingMonitor {
/** The language client being monitored */
private readonly _client: LanguageClient;
/** The configuration setting to monitor */
private readonly _setting: string;
/**
* Create a setting monitor for a language client
* @param client - The language client to manage
* @param setting - The configuration setting key to monitor
*/
constructor(client: LanguageClient, setting: string);
/**
* Start monitoring the configuration setting
* @returns Disposable to stop monitoring
*/
start(): Disposable;
}Usage Examples:
Monitor a setting to control client lifecycle:
import { SettingMonitor } from "vscode-languageclient/node";
const client = new LanguageClient(/* ... */);
const monitor = new SettingMonitor(client, 'myExtension.enable');
// Start monitoring - client will start/stop based on setting value
const disposable = monitor.start();
// Stop monitoring when extension is deactivated
context.subscriptions.push(disposable);Specialized error types for Language Server Protocol operations.
/**
* LSP-specific cancellation error with additional data
*/
class LSPCancellationError extends Error {
/** Additional error data from LSP */
readonly data: any;
/**
* Create an LSP cancellation error
* @param data - Error data from the language server
*/
constructor(data: any);
}
/**
* Standard VS Code cancellation error
*/
class CancellationError extends Error {
constructor();
}Usage Examples:
Handle LSP-specific errors:
try {
const result = await client.sendRequest('custom/method', params);
} catch (error) {
if (error instanceof LSPCancellationError) {
console.log('Request was cancelled by LSP server:', error.data);
} else if (error instanceof CancellationError) {
console.log('Request was cancelled by VS Code');
} else {
console.error('Request failed:', error);
}
}Utility classes for converting between VS Code types and LSP protocol types.
/**
* Converter for transforming VS Code types to LSP protocol types
*/
class Code2ProtocolConverter {
/** Convert VS Code URI to protocol string */
asUri(uri: Uri): string;
/** Convert VS Code document URI to protocol DocumentUri */
asTextDocumentIdentifier(textDocument: TextDocument): TextDocumentIdentifier;
/** Convert VS Code document to protocol VersionedTextDocumentIdentifier */
asVersionedTextDocumentIdentifier(textDocument: TextDocument): VersionedTextDocumentIdentifier;
/** Convert VS Code position to protocol Position */
asPosition(position: Position): Position;
/** Convert VS Code range to protocol Range */
asRange(range: Range): Range;
/** Convert VS Code location to protocol Location */
asLocation(location: Location): Location;
/** Convert VS Code diagnostic severity to protocol DiagnosticSeverity */
asDiagnosticSeverity(severity: DiagnosticSeverity): lsp.DiagnosticSeverity;
/** Convert VS Code diagnostic to protocol Diagnostic */
asDiagnostic(diagnostic: Diagnostic): lsp.Diagnostic;
/** Convert VS Code text edit to protocol TextEdit */
asTextEdit(edit: TextEdit): lsp.TextEdit;
/** Convert VS Code workspace edit to protocol WorkspaceEdit */
asWorkspaceEdit(workspaceEdit: WorkspaceEdit): lsp.WorkspaceEdit;
/** Convert VS Code completion item to protocol CompletionItem */
asCompletionItem(item: CompletionItem): lsp.CompletionItem;
/** Convert VS Code symbol information to protocol SymbolInformation */
asSymbolInformation(symbol: SymbolInformation): lsp.SymbolInformation;
/** Convert VS Code code action to protocol CodeAction */
asCodeAction(action: CodeAction): lsp.CodeAction;
/** Convert VS Code code lens to protocol CodeLens */
asCodeLens(lens: CodeLens): lsp.CodeLens;
}
/**
* Converter for transforming LSP protocol types to VS Code types
*/
class Protocol2CodeConverter {
/** Convert protocol URI string to VS Code URI */
asUri(uri: string): Uri;
/** Convert protocol Position to VS Code Position */
asPosition(position: lsp.Position): Position;
/** Convert protocol Range to VS Code Range */
asRange(range: lsp.Range): Range;
/** Convert protocol Location to VS Code Location */
asLocation(location: lsp.Location): Location;
/** Convert protocol DiagnosticSeverity to VS Code DiagnosticSeverity */
asDiagnosticSeverity(severity: lsp.DiagnosticSeverity | undefined): DiagnosticSeverity;
/** Convert protocol DiagnosticTag to VS Code DiagnosticTag */
asDiagnosticTag(tag: lsp.DiagnosticTag): DiagnosticTag;
/** Convert protocol Diagnostic to VS Code Diagnostic */
asDiagnostic(diagnostic: lsp.Diagnostic): Diagnostic;
/** Convert protocol TextEdit to VS Code TextEdit */
asTextEdit(edit: lsp.TextEdit): TextEdit;
/** Convert protocol WorkspaceEdit to VS Code WorkspaceEdit */
asWorkspaceEdit(workspaceEdit: lsp.WorkspaceEdit): WorkspaceEdit;
/** Convert protocol CompletionItem to VS Code CompletionItem */
asCompletionItem(item: lsp.CompletionItem): CompletionItem;
/** Convert protocol Hover to VS Code Hover */
asHover(hover: lsp.Hover): Hover;
/** Convert protocol SignatureHelp to VS Code SignatureHelp */
asSignatureHelp(signatureHelp: lsp.SignatureHelp): SignatureHelp;
/** Convert protocol DocumentSymbol to VS Code DocumentSymbol */
asDocumentSymbol(symbol: lsp.DocumentSymbol): DocumentSymbol;
/** Convert protocol SymbolInformation to VS Code SymbolInformation */
asSymbolInformation(symbol: lsp.SymbolInformation): SymbolInformation;
/** Convert protocol CodeAction to VS Code CodeAction */
asCodeAction(action: lsp.CodeAction): CodeAction;
/** Convert protocol CodeLens to VS Code CodeLens */
asCodeLens(lens: lsp.CodeLens): CodeLens;
/** Convert protocol DocumentHighlight to VS Code DocumentHighlight */
asDocumentHighlight(highlight: lsp.DocumentHighlight): DocumentHighlight;
/** Convert protocol DocumentLink to VS Code DocumentLink */
asDocumentLink(link: lsp.DocumentLink): DocumentLink;
/** Convert protocol SelectionRange to VS Code SelectionRange */
asSelectionRange(range: lsp.SelectionRange): SelectionRange;
/** Convert protocol SemanticTokens to VS Code SemanticTokens */
asSemanticTokens(tokens: lsp.SemanticTokens): SemanticTokens;
/** Convert protocol InlayHint to VS Code InlayHint */
asInlayHint(hint: lsp.InlayHint): InlayHint;
}Usage Examples:
Using converters in middleware:
const clientOptions: LanguageClientOptions = {
middleware: {
completion: {
provideCompletionItem: (document, position, context, token, next) => {
// Access converters from client
const c2p = client.code2ProtocolConverter;
const p2c = client.protocol2CodeConverter;
// Convert position to protocol format
const protocolPosition = c2p.asPosition(position);
// Get completions and convert back
return next(document, position, context, token).then(result => {
if (Array.isArray(result)) {
return result.map(item => {
// Customize completion item
return { ...item, sortText: '0' + item.label };
});
}
return result;
});
}
}
}
};Utilities for file system operations and path handling.
/**
* File formatting options interface
*/
interface FileFormattingOptions {
/** Insert final newline */
insertFinalNewline?: boolean;
/** Trim final newlines */
trimFinalNewlines?: boolean;
/** Trim trailing whitespace */
trimTrailingWhitespace?: boolean;
}Helper classes for managing asynchronous operations.
/**
* Utility for delaying execution of operations
*/
class Delayer<T> {
/** Default delay in milliseconds */
readonly defaultDelay: number;
/**
* Create a delayer with default delay
* @param defaultDelay - Default delay in milliseconds
*/
constructor(defaultDelay: number);
/**
* Trigger execution after delay
* @param task - Task to execute
* @param delay - Custom delay (uses default if not provided)
* @returns Promise that resolves with task result
*/
trigger(task: () => T | Promise<T>, delay?: number): Promise<T>;
/** Cancel pending execution */
cancel(): void;
/** Whether execution is pending */
isTriggered(): boolean;
}
/**
* Utility for controlling concurrent access to resources
*/
class Semaphore {
/** Number of available permits */
readonly size: number;
/**
* Create a semaphore with specified size
* @param size - Number of concurrent operations allowed
*/
constructor(size: number);
/**
* Acquire a permit and execute task
* @param task - Task to execute with permit
* @returns Promise that resolves with task result
*/
lock<T>(task: () => Promise<T>): Promise<T>;
}Usage Examples:
Using Delayer for batching operations:
import { Delayer } from "vscode-languageclient/node";
class DiagnosticsManager {
private _delayer = new Delayer<void>(500);
private _pendingDiagnostics: Diagnostic[] = [];
addDiagnostic(diagnostic: Diagnostic): void {
this._pendingDiagnostics.push(diagnostic);
// Batch diagnostics updates with 500ms delay
this._delayer.trigger(() => {
const diagnostics = [...this._pendingDiagnostics];
this._pendingDiagnostics = [];
return this.publishDiagnostics(diagnostics);
});
}
private async publishDiagnostics(diagnostics: Diagnostic[]): Promise<void> {
// Publish batched diagnostics
}
}Using Semaphore for resource control:
import { Semaphore } from "vscode-languageclient/node";
class RequestManager {
private _semaphore = new Semaphore(3); // Max 3 concurrent requests
async sendRequest<T>(request: () => Promise<T>): Promise<T> {
return this._semaphore.lock(request);
}
}Utility for generating unique identifiers.
/**
* Generate a UUID v4 string
* @returns UUID v4 string
*/
function generateUuid(): string;
/**
* Generate a short UUID (8 characters)
* @returns Short UUID string
*/
function generateShortUuid(): string;Usage Examples:
Generate request IDs:
import * as UUID from "vscode-languageclient/lib/common/utils/uuid";
const requestId = UUID.generateUuid();
const shortId = UUID.generateShortUuid();
await client.sendRequest('custom/method', {
id: requestId,
data: 'example'
});Utility functions for runtime type checking.
/**
* Type guard utilities
*/
namespace Is {
/** Check if value is a string */
function string(value: any): value is string;
/** Check if value is a number */
function number(value: any): value is number;
/** Check if value is a boolean */
function boolean(value: any): value is boolean;
/** Check if value is undefined */
function undefined(value: any): value is undefined;
/** Check if value is defined (not undefined) */
function defined<T>(value: T | undefined): value is T;
/** Check if value is a function */
function func(value: any): value is Function;
/** Check if value is an object */
function objectLiteral(value: any): value is object;
}Usage Examples:
Safe type checking:
import * as Is from "vscode-languageclient/lib/common/utils/is";
function processConfig(config: any): void {
if (Is.string(config.serverPath)) {
// config.serverPath is guaranteed to be string
const serverOptions = { command: config.serverPath };
}
if (Is.number(config.maxConcurrency) && config.maxConcurrency > 0) {
// config.maxConcurrency is guaranteed to be positive number
const semaphore = new Semaphore(config.maxConcurrency);
}
}Install with Tessl CLI
npx tessl i tessl/npm-vscode-languageclient