CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vscode-languageserver

Language Server Protocol implementation for Node.js providing comprehensive LSP server capabilities including text synchronization, diagnostics, code completion, and workspace symbol searching

Pending
Overview
Eval results
Files

utilities.mddocs/

Utility Classes

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.

Capabilities

Semantic Tokens Builder

Helper class for building semantic tokens responses with proper encoding and delta support.

/**
 * A helper class to build semantic tokens data arrays
 */
class SemanticTokensBuilder {
    /** Create a new semantic tokens builder */
    constructor();
    
    /**
     * Add a semantic token to the builder
     * @param line Line number (0-based)
     * @param char Character offset on line (0-based)  
     * @param length Length of the token in characters
     * @param tokenType Token type index from legend
     * @param tokenModifiers Token modifiers as bit flags from legend
     */
    push(line: number, char: number, length: number, tokenType: number, tokenModifiers?: number): void;
    
    /**
     * Build the final semantic tokens result
     * @returns SemanticTokens object with encoded data array
     */
    build(): SemanticTokens;
    
    /**
     * Build semantic tokens edits for delta requests
     * @returns SemanticTokensEdits for incremental updates
     */
    buildEdits(): SemanticTokensEdits;
}

Usage Example:

import { SemanticTokensBuilder } from "vscode-languageserver";

// Define token types and modifiers indices (should match client capabilities)
const TokenTypes = {
    keyword: 0,
    function: 1,
    variable: 2,
    string: 3,
    number: 4,
    comment: 5
};

const TokenModifiers = {
    declaration: 1 << 0,
    definition: 1 << 1,
    readonly: 1 << 2,
    static: 1 << 3,
    deprecated: 1 << 4
};

connection.languages.semanticTokens.on((params) => {
    const document = documents.get(params.textDocument.uri);
    if (!document) return null;
    
    const builder = new SemanticTokensBuilder();
    
    const lines = document.getText().split('\n');
    lines.forEach((line, lineIndex) => {
        // Simple tokenization example
        const words = line.split(/\s+/);
        let charOffset = 0;
        
        words.forEach(word => {
            const wordStart = line.indexOf(word, charOffset);
            
            if (word === 'function') {
                builder.push(lineIndex, wordStart, word.length, TokenTypes.keyword, TokenModifiers.declaration);
            } else if (word.startsWith('"') && word.endsWith('"')) {
                builder.push(lineIndex, wordStart, word.length, TokenTypes.string);
            } else if (/^\d+$/.test(word)) {
                builder.push(lineIndex, wordStart, word.length, TokenTypes.number);
            } else if (word.startsWith('//')) {
                builder.push(lineIndex, wordStart, line.length - wordStart, TokenTypes.comment);
                break; // Rest of line is comment
            }
            
            charOffset = wordStart + word.length;
        });
    });
    
    return builder.build();
});

Error Message Tracker

Helper class for tracking and deduplicating error messages before sending them to the client.

/**
 * Helps tracking error message. Equal occurrences of the same message are only stored once.
 * This class is useful if text documents are validated in a loop and equal error messages
 * should be folded into one.
 */
class ErrorMessageTracker {
    /** Create a new error message tracker */
    constructor();
    
    /**
     * Add a message to the tracker
     * @param message The message to add
     */
    add(message: string): void;
    
    /**
     * Send all tracked messages to the connection's window
     * @param connection The connection established between client and server
     */
    sendErrors(connection: { window: RemoteWindow }): void;
}

Usage Example:

import { ErrorMessageTracker } from "vscode-languageserver";

const errorTracker = new ErrorMessageTracker();

// Validate multiple documents
documents.all().forEach(document => {
    try {
        validateDocument(document);
    } catch (error) {
        // Add errors to tracker (duplicates will be deduplicated)
        errorTracker.add(`Error in ${document.uri}: ${error.message}`);
    }
});

// Send all unique errors to client
errorTracker.sendErrors(connection);

Progress Reporting

Interfaces for reporting work done progress and partial results during long-running operations.

/**
 * Interface for reporting work done progress
 */
interface WorkDoneProgressReporter {
    /**
     * Begin progress reporting
     * @param title The title of the progress
     * @param percentage Optional initial percentage (0-100)
     * @param message Optional initial message
     * @param cancellable Whether the operation can be cancelled
     */
    begin(title: string, percentage?: number, message?: string, cancellable?: boolean): void;
    
    /**
     * Report progress with percentage
     * @param percentage Progress percentage (0-100)
     */
    report(percentage: number): void;
    
    /**
     * Report progress with message
     * @param message Progress message
     */
    report(message: string): void;
    
    /**
     * Report progress with percentage and message
     * @param percentage Progress percentage (0-100)
     * @param message Optional progress message
     */
    report(percentage: number, message?: string): void;
    
    /** Complete the progress reporting */
    done(): void;
}

/**
 * Server-side work done progress reporter
 */
interface WorkDoneProgressServerReporter extends WorkDoneProgressReporter {
    /** The progress token */
    readonly token: ProgressToken;
}

/**
 * Interface for reporting partial results
 */
interface ResultProgressReporter<T> {
    /**
     * Report partial result data
     * @param data The partial result data
     */
    report(data: T): void;
}

Usage Example:

connection.onWorkspaceSymbol((params, token, workDoneProgress, resultProgress) => {
    // Begin progress reporting
    workDoneProgress.begin('Searching workspace symbols');
    
    const allSymbols: SymbolInformation[] = [];
    const files = getAllWorkspaceFiles();
    
    files.forEach((file, index) => {
        // Report progress
        const percentage = Math.round((index / files.length) * 100);
        workDoneProgress.report(percentage, `Processing ${file}`);
        
        // Get symbols from file
        const symbols = getSymbolsFromFile(file, params.query);
        allSymbols.push(...symbols);
        
        // Report partial results
        if (symbols.length > 0) {
            resultProgress.report(symbols);
        }
        
        // Check for cancellation
        if (token.isCancellationRequested) {
            workDoneProgress.done();
            return null;
        }
    });
    
    // Complete progress
    workDoneProgress.done();
    return allSymbols;
});

Registration Utilities

Interfaces for bulk registration and unregistration of capabilities.

/**
 * Interface for bulk capability registration
 */
interface BulkRegistration {
    /**
     * Add a registration to the bulk registration
     * @param type The request/notification type to register
     * @param registerOptions Optional registration options
     */
    add<RO>(type: ProtocolRequestType0<any, any, any, RO> | ProtocolNotificationType0<RO>, registerOptions?: RO): void;
    add<P, RO>(type: ProtocolRequestType<P, any, any, any, RO> | ProtocolNotificationType<P, RO>, registerOptions?: RO): void;
    
    /**
     * Convert to registration parameters for sending to client
     */
    asRegistrationParams(): RegistrationParams;
}

/**
 * Interface for bulk capability unregistration  
 */
interface BulkUnregistration extends Disposable {
    /**
     * Add an unregistration to the bulk unregistration
     * @param unregistration The unregistration to add
     */
    add(unregistration: Unregistration): void;
    
    /**
     * Convert to unregistration parameters for sending to client
     */
    asUnregistrationParams(): UnregistrationParams;
    
    /** Dispose all registrations */
    dispose(): void;
}

Usage Example:

// Bulk register multiple capabilities
const bulkRegistration = BulkRegistration.create();
bulkRegistration.add(HoverRequest.type, { documentSelector: [{ scheme: 'file', language: 'typescript' }] });
bulkRegistration.add(CompletionRequest.type, { documentSelector: [{ scheme: 'file', language: 'typescript' }] });
bulkRegistration.add(DefinitionRequest.type, { documentSelector: [{ scheme: 'file', language: 'typescript' }] });

// Send bulk registration to client
connection.client.register(bulkRegistration.asRegistrationParams());

// Later, bulk unregister
const bulkUnregistration = BulkUnregistration.create();
bulkUnregistration.add({ id: 'hover-registration', method: 'textDocument/hover' });
bulkUnregistration.add({ id: 'completion-registration', method: 'textDocument/completion' });

connection.client.unregister(bulkUnregistration.asUnregistrationParams());

UUID Utilities

Simple UUID generation utilities for creating unique identifiers.

/**
 * UUID generation utilities
 */
namespace UUID {
    /**
     * Generate a new UUID v4
     * @returns A new UUID string
     */
    function generateUuid(): string;
}

Usage Example:

import { UUID } from "vscode-languageserver";

// Generate unique identifiers for registrations
const registrationId = UUID.generateUuid();
const workDoneToken = UUID.generateUuid();

connection.client.register({
    registrations: [{
        id: registrationId,
        method: 'textDocument/hover',
        registerOptions: { documentSelector: [{ scheme: 'file' }] }
    }]
});

Type Checking Utilities

Utility functions for runtime type checking and validation.

/**
 * Type checking utilities
 */
namespace Is {
    /** Check if value is a function */
    function func(value: any): value is Function;
    
    /** 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;
}

Usage Example:

import { Is } from "vscode-languageserver";

function processValue(value: any) {
    if (Is.string(value)) {
        // TypeScript knows value is string here
        return value.toUpperCase();
    } else if (Is.number(value)) {
        // TypeScript knows value is number here
        return value.toString();
    } else if (Is.defined(value)) {
        // TypeScript knows value is not undefined here
        return JSON.stringify(value);
    }
    return 'undefined';
}

Core Types

interface SemanticTokens {
    /** An optional result id */
    resultId?: string;
    /** The actual tokens */
    data: number[];
}

interface SemanticTokensEdits {
    /** An optional result id */
    resultId?: string;
    /** The edits to transform a previous result into a new result */
    edits: SemanticTokensEdit[];
}

interface SemanticTokensEdit {
    /** The start offset of the edit */
    start: number;
    /** The count of elements to remove */
    deleteCount: number;
    /** The elements to insert */
    data?: number[];
}

interface RemoteWindow {
    /** Show an error message */
    showErrorMessage(message: string): void;
    /** Show a warning message */
    showWarningMessage(message: string): void;
    /** Show an information message */
    showInformationMessage(message: string): void;
}

type ProgressToken = number | string;

interface RegistrationParams {
    registrations: Registration[];
}

interface Registration {
    /** The id used to register the request */
    id: string;
    /** The method to register for */
    method: string;
    /** Options necessary for the registration */
    registerOptions?: any;
}

interface UnregistrationParams {
    unregisterations: Unregistration[];
}

interface Unregistration {
    /** The id used to unregister the request or notification */
    id: string;
    /** The method to unregister for */
    method: string;
}

interface Disposable {
    dispose(): void;
}

Install with Tessl CLI

npx tessl i tessl/npm-vscode-languageserver

docs

connection.md

documents.md

extended-features.md

index.md

language-features.md

notebooks.md

utilities.md

tile.json