CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vscode-languageclient

VSCode Language Server Protocol client implementation for extension integration with language servers

Pending
Overview
Eval results
Files

feature-system.mddocs/

Feature System

Base interfaces and classes for implementing static and dynamic language server features.

Capabilities

Core Feature Interfaces

Base interfaces defining the feature system architecture.

/**
 * Base interface for all language client features
 */
interface Feature {
  /** Get the current state of the feature */
  getState(): FeatureState;
  
  /** Clear the feature state and cleanup resources */
  clear(): void;
}

/**
 * Feature state enumeration
 */
enum FeatureState {
  /** Feature is not initialized */
  Initial,
  /** Feature is starting */
  Starting,
  /** Feature is active and running */
  Started,
  /** Feature has been stopped */
  Stopped,
  /** Feature is disposing */
  Disposing,
  /** Feature has been disposed */
  Disposed
}

Static Features

Features that are registered once during client initialization.

/**
 * Interface for static language server features
 * Static features are registered once during initialization and don't support dynamic registration
 */
interface StaticFeature extends Feature {
  /** 
   * Fill initialization parameters sent to the server
   * Called before client initialization
   */
  fillInitializeParams?(params: InitializeParams): void;
  
  /** 
   * Fill client capabilities advertised to the server
   * Called during client initialization
   */
  fillClientCapabilities(capabilities: ClientCapabilities): void;
  
  /** 
   * Pre-initialization hook called before the feature is initialized
   * Used for setup that requires server capabilities
   */
  preInitialize?(capabilities: ServerCapabilities, documentSelector: DocumentSelector | undefined): void;
  
  /** 
   * Initialize the feature with server capabilities
   * Called after successful server initialization
   */
  initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector | undefined): void;
}

Usage Examples:

Implementing a static feature:

class CustomStaticFeature implements StaticFeature {
  private _state: FeatureState = FeatureState.Initial;
  
  fillClientCapabilities(capabilities: ClientCapabilities): void {
    // Add custom capabilities
    capabilities.experimental = {
      ...capabilities.experimental,
      customFeature: true
    };
  }
  
  initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector | undefined): void {
    if (capabilities.experimental?.customFeature) {
      this._state = FeatureState.Started;
      // Initialize feature logic
    }
  }
  
  getState(): FeatureState {
    return this._state;
  }
  
  clear(): void {
    this._state = FeatureState.Disposed;
    // Cleanup resources
  }
}

Dynamic Features

Features that support dynamic registration and unregistration.

/**
 * Interface for dynamic language server features
 * Dynamic features can be registered and unregistered at runtime
 */
interface DynamicFeature<RO> extends StaticFeature {
  /** Registration type that identifies this feature */
  readonly registrationType: RegistrationType<RO>;
  
  /** 
   * Register the feature with specific options
   * Called when server requests feature registration
   */
  register(data: RegistrationData<RO>): void;
  
  /** 
   * Unregister the feature by ID
   * Called when server requests feature unregistration
   */
  unregister(id: string): void;
}

/**
 * Registration data for dynamic features
 */
interface RegistrationData<T> {
  /** Unique registration identifier */
  id: string;
  /** Registration method name */
  method: string;
  /** Registration options specific to the feature */
  registerOptions?: T;
}

/**
 * Registration type interface
 */
interface RegistrationType<RO> {
  /** LSP method name for this registration type */
  readonly method: string;
}

Usage Examples:

Implementing a dynamic feature:

class CustomDynamicFeature implements DynamicFeature<CustomRegistrationOptions> {
  private _state: FeatureState = FeatureState.Initial;
  private _registrations = new Map<string, CustomRegistration>();
  
  readonly registrationType: RegistrationType<CustomRegistrationOptions> = {
    method: 'textDocument/customFeature'
  };
  
  fillClientCapabilities(capabilities: ClientCapabilities): void {
    capabilities.textDocument = capabilities.textDocument || {};
    capabilities.textDocument.customFeature = {
      dynamicRegistration: true
    };
  }
  
  initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector | undefined): void {
    this._state = FeatureState.Started;
  }
  
  register(data: RegistrationData<CustomRegistrationOptions>): void {
    const registration = new CustomRegistration(data.id, data.registerOptions);
    this._registrations.set(data.id, registration);
  }
  
  unregister(id: string): void {
    const registration = this._registrations.get(id);
    if (registration) {
      registration.dispose();
      this._registrations.delete(id);
    }
  }
  
  getState(): FeatureState {
    return this._state;
  }
  
  clear(): void {
    for (const registration of this._registrations.values()) {
      registration.dispose();
    }
    this._registrations.clear();
    this._state = FeatureState.Disposed;
  }
}

Provider Features

Specialized interfaces for features that provide VS Code providers.

/**
 * Mixin interface for text document provider features
 * Features implementing this can provide providers per text document
 */
interface TextDocumentProviderFeature<T> {
  /** 
   * Get the provider for a specific text document
   * @param textDocument - The text document to get provider for
   * @returns Provider instance or undefined if not available
   */
  getProvider(textDocument: TextDocument): T | undefined;
}

/**
 * Interface for workspace provider features
 * Features implementing this provide workspace-wide functionality
 */
interface WorkspaceProviderFeature<PR> {
  /** 
   * Get all providers registered for workspace operations
   * @returns Array of provider instances or undefined
   */
  getProviders(): PR[] | undefined;
}

/**
 * Interface for features that can be client-specific
 */
interface FeatureClient<M, PO> {
  /** Get the method name for this feature */
  getRegistrationType(): RegistrationType<PO>;
  
  /** Fill client capabilities for this feature */
  fillClientCapabilities(capabilities: ClientCapabilities): void;
  
  /** Initialize the feature */
  initialize(capabilities: M, documentSelector: DocumentSelector | undefined): void;
  
  /** Register options for dynamic registration */
  register(data: RegistrationData<PO>): void;
  
  /** Unregister by ID */
  unregister(id: string): void;
  
  /** Dispose the feature */
  dispose(): void;
}

Text Document Features

Base classes for text document-based features.

/**
 * Base class for text document synchronization features
 */
abstract class TextDocumentSendFeature<T> implements DynamicFeature<T> {
  protected _client: BaseLanguageClient;
  protected _registrationType: RegistrationType<T>;
  
  constructor(client: BaseLanguageClient, registrationType: RegistrationType<T>);
  
  abstract fillClientCapabilities(capabilities: ClientCapabilities): void;
  abstract initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector | undefined): void;
  
  register(data: RegistrationData<T>): void;
  unregister(id: string): void;
  getState(): FeatureState;
  clear(): void;
  
  get registrationType(): RegistrationType<T>;
}

/**
 * Interface for features that send text document notifications
 */
interface DidOpenTextDocumentFeatureShape {
  /** Handle document open event */
  didOpen(textDocument: TextDocument): void;
}

/**
 * Interface for features that send text document close notifications
 */
interface DidCloseTextDocumentFeatureShape {
  /** Handle document close event */
  didClose(textDocument: TextDocument): void;
}

/**
 * Interface for features that send text document change notifications
 */
interface DidChangeTextDocumentFeatureShape {
  /** Handle document change event */
  didChange(event: TextDocumentChangeEvent): void;
}

/**
 * Interface for features that send text document save notifications
 */
interface DidSaveTextDocumentFeatureShape {
  /** Handle document save event */
  didSave(textDocument: TextDocument): void;
}

Provider Shape Interfaces

Standard shapes for VS Code language providers.

/**
 * Shape interface for code lens providers
 */
interface CodeLensProviderShape {
  /** Provide code lenses for a document */
  provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult<CodeLens[]>;
  
  /** Resolve a code lens */
  resolveCodeLens?(codeLens: CodeLens, token: CancellationToken): ProviderResult<CodeLens>;
  
  /** Event fired when code lenses should be refreshed */
  onDidChangeCodeLenses?: Event<void>;
}

/**
 * Shape interface for diagnostic providers
 */
interface DiagnosticProviderShape {
  /** Event fired when diagnostics change */
  onDidChangeDiagnostics: Event<void>;
  
  /** Provide diagnostics for a document */
  provideDiagnostics(
    document: TextDocument | Uri, 
    previousResultId: string | undefined, 
    token: CancellationToken
  ): ProviderResult<DocumentDiagnosticReport>;
  
  /** Provide workspace diagnostics */
  provideWorkspaceDiagnostics?(
    resultIds: PreviousResultId[], 
    token: CancellationToken, 
    resultReporter: ResultReporter
  ): ProviderResult<WorkspaceDiagnosticReport>;
}

/**
 * Shape interface for semantic tokens providers
 */
interface SemanticTokensProviderShape {
  /** Provide semantic tokens for entire document */
  provideDocumentSemanticTokens(document: TextDocument, token: CancellationToken): ProviderResult<SemanticTokens>;
  
  /** Provide semantic token edits */
  provideDocumentSemanticTokensEdits?(
    document: TextDocument, 
    previousResultId: string, 
    token: CancellationToken
  ): ProviderResult<SemanticTokensEdits | SemanticTokens>;
  
  /** Provide semantic tokens for a range */
  provideDocumentRangeSemanticTokens?(
    document: TextDocument, 
    range: Range, 
    token: CancellationToken
  ): ProviderResult<SemanticTokens>;
}

/**
 * Shape interface for inlay hints providers
 */
interface InlayHintsProviderShape {
  /** Provide inlay hints for a document range */
  provideInlayHints(document: TextDocument, range: Range, token: CancellationToken): ProviderResult<InlayHint[]>;
  
  /** Resolve an inlay hint */
  resolveInlayHint?(item: InlayHint, token: CancellationToken): ProviderResult<InlayHint>;
  
  /** Event fired when inlay hints should be refreshed */
  onDidChangeInlayHints?: Event<void>;
}

/**
 * Shape interface for inline value providers  
 */
interface InlineValueProviderShape {
  /** Provide inline values for a document range */
  provideInlineValues(
    document: TextDocument, 
    viewPort: Range, 
    context: InlineValueContext, 
    token: CancellationToken
  ): ProviderResult<InlineValue[]>;
  
  /** Event fired when inline values should be refreshed */
  onDidChangeInlineValues?: Event<void>;
}

Feature Registration

Utilities for feature registration and management.

/**
 * Ensure a registration data object is properly formatted
 */
function ensure<T>(target: T, key: keyof T): T[typeof key];

/**
 * Language Server Protocol cancellation error
 */
class LSPCancellationError extends Error {
  /** LSP error data */
  readonly data: any;
  
  constructor(data: any);
}

/**
 * Create all proposed features for experimental LSP capabilities
 */
function createAll(): (StaticFeature | DynamicFeature<any>)[];

Usage Examples:

Registering features with client:

import { LanguageClient } from "vscode-languageclient/node";

const client = new LanguageClient(/* ... */);

// Register a static feature
const staticFeature = new CustomStaticFeature();
client.registerFeature(staticFeature);

// Register a dynamic feature
const dynamicFeature = new CustomDynamicFeature();
client.registerFeature(dynamicFeature);

// Register proposed features
const proposedFeatures = createAll();
proposedFeatures.forEach(feature => client.registerFeature(feature));

Feature with provider interface:

class CustomProviderFeature implements DynamicFeature<CustomOptions>, TextDocumentProviderFeature<CustomProvider> {
  private _providers = new Map<string, CustomProvider>();
  
  getProvider(textDocument: TextDocument): CustomProvider | undefined {
    // Find appropriate provider for document
    for (const provider of this._providers.values()) {
      if (provider.canHandle(textDocument)) {
        return provider;
      }
    }
    return undefined;
  }
  
  register(data: RegistrationData<CustomOptions>): void {
    const provider = new CustomProvider(data.registerOptions);
    this._providers.set(data.id, provider);
  }
  
  // ... implement other DynamicFeature methods
}

Install with Tessl CLI

npx tessl i tessl/npm-vscode-languageclient

docs

configuration.md

core-client.md

feature-system.md

index.md

language-features.md

transport.md

utilities.md

tile.json