Skip to content

Commit

Permalink
Support that servers can register a text content provider for multipl…
Browse files Browse the repository at this point in the history
…e schemes (#1539)
  • Loading branch information
dbaeumer committed Aug 12, 2024
1 parent f8c8b09 commit a68fdf0
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 14 deletions.
2 changes: 1 addition & 1 deletion client-node-tests/src/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ suite('Client integration', () => {
willDelete: { filters: [{ scheme: fsProvider.scheme, pattern: { glob: '**/deleted-static/**{/,/*.txt}' } }] },
},
textDocumentContent: {
scheme: 'content-test'
schemes: ['content-test']
}
},
linkedEditingRangeProvider: true,
Expand Down
2 changes: 1 addition & 1 deletion client-node-tests/src/servers/testServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ connection.onInitialize((params: InitializeParams): any => {
},
},
textDocumentContent: {
scheme: 'content-test'
schemes: ['content-test']
}
},
linkedEditingRangeProvider: true,
Expand Down
69 changes: 60 additions & 9 deletions client/src/common/textDocumentContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
* ------------------------------------------------------------------------------------------ */

import * as vscode from 'vscode';
import { StaticRegistrationOptions, TextDocumentContentRefreshRequest, TextDocumentContentRequest, type ClientCapabilities, type ServerCapabilities, type TextDocumentContentParams, type TextDocumentContentRegistrationOptions } from 'vscode-languageserver-protocol';
import { StaticRegistrationOptions, TextDocumentContentRefreshRequest, TextDocumentContentRequest, type ClientCapabilities, type RegistrationType, type ServerCapabilities, type TextDocumentContentParams, type TextDocumentContentRegistrationOptions } from 'vscode-languageserver-protocol';

import { WorkspaceFeature, ensure, type FeatureClient } from './features';
import { ensure, type DynamicFeature, type FeatureClient, type FeatureState, type RegistrationData } from './features';
import * as UUID from './utils/uuid';


Expand All @@ -19,14 +19,35 @@ export interface TextDocumentContentMiddleware {
}

export interface TextDocumentContentProviderShape {
provider: vscode.TextDocumentContentProvider;
scheme: string;
onDidChangeEmitter: vscode.EventEmitter<vscode.Uri>;
provider: vscode.TextDocumentContentProvider;
}

export class TextDocumentContentFeature extends WorkspaceFeature<TextDocumentContentRegistrationOptions, TextDocumentContentProviderShape, TextDocumentContentMiddleware> {
export class TextDocumentContentFeature implements DynamicFeature<TextDocumentContentRegistrationOptions> {

private readonly _client: FeatureClient<TextDocumentContentMiddleware>;
private readonly _registrations: Map<string, { disposable: vscode.Disposable; providers: TextDocumentContentProviderShape[] }> = new Map();

constructor(client: FeatureClient<TextDocumentContentMiddleware>) {
super(client, TextDocumentContentRequest.type);
this._client = client;
}

public getState(): FeatureState {
const registrations = this._registrations.size > 0;
return { kind: 'workspace', id: TextDocumentContentRequest.method, registrations };
}

public get registrationType(): RegistrationType<TextDocumentContentRegistrationOptions> {
return TextDocumentContentRequest.type;
}

public getProviders(): TextDocumentContentProviderShape[] {
const result: TextDocumentContentProviderShape[] = [];
for (const registration of this._registrations.values()) {
result.push(...registration.providers);
}
return result;
}

public fillClientCapabilities(capabilities: ClientCapabilities): void {
Expand All @@ -38,8 +59,12 @@ export class TextDocumentContentFeature extends WorkspaceFeature<TextDocumentCon
const client = this._client;
client.onRequest(TextDocumentContentRefreshRequest.type, async (params) => {
const uri = client.protocol2CodeConverter.asUri(params.uri);
for (const provider of this.getProviders()) {
provider.onDidChangeEmitter.fire(uri);
for (const registrations of this._registrations.values()) {
for (const provider of registrations.providers) {
if (provider.scheme !== uri.scheme) {
provider.onDidChangeEmitter.fire(uri);
}
}
}
});

Expand All @@ -54,7 +79,18 @@ export class TextDocumentContentFeature extends WorkspaceFeature<TextDocumentCon
});
}

protected registerLanguageProvider(options: TextDocumentContentRegistrationOptions): [vscode.Disposable, TextDocumentContentProviderShape] {
public register(data: RegistrationData<TextDocumentContentRegistrationOptions>): void {
const registrations: TextDocumentContentProviderShape[] = [];
const disposables: vscode.Disposable[] = [];
for (const scheme of data.registerOptions.schemes) {
const [disposable, registration] = this.registerTextDocumentContentProvider(scheme);
registrations.push(registration);
disposables.push(disposable);
}
this._registrations.set(data.id, { disposable: vscode.Disposable.from(...disposables), providers: registrations });
}

private registerTextDocumentContentProvider(scheme: string): [vscode.Disposable, TextDocumentContentProviderShape] {
const eventEmitter: vscode.EventEmitter<vscode.Uri> = new vscode.EventEmitter<vscode.Uri>();
const provider: vscode.TextDocumentContentProvider = {
onDidChange: eventEmitter.event,
Expand All @@ -79,6 +115,21 @@ export class TextDocumentContentFeature extends WorkspaceFeature<TextDocumentCon
: provideTextDocumentContent(uri, token);
}
};
return [vscode.workspace.registerTextDocumentContentProvider(options.scheme, provider), { provider, onDidChangeEmitter: eventEmitter }];
return [vscode.workspace.registerTextDocumentContentProvider(scheme, provider), { scheme, onDidChangeEmitter: eventEmitter, provider }];
}

public unregister(id: string): void {
const registration = this._registrations.get(id);
if (registration !== undefined) {
this._registrations.delete(id);
registration.disposable.dispose();
}
}

public clear(): void {
this._registrations.forEach((registration) => {
registration.disposable.dispose();
});
this._registrations.clear();
}
}
4 changes: 2 additions & 2 deletions protocol/src/common/protocol.textDocumentContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ export type TextDocumentContentClientCapabilities = {
*/
export type TextDocumentContentOptions = {
/**
* The scheme for which the server provides content.
* The schemes for which the server provides content.
*/
scheme: string;
schemes: string[];
};

/**
Expand Down
2 changes: 1 addition & 1 deletion testbed/server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ connection.onInitialize((params, cancel, progress): Thenable<InitializeResult> |
changeNotifications: true
},
textDocumentContent: {
scheme: 'test-content'
schemes: ['test-content']
}
},
implementationProvider: {
Expand Down

0 comments on commit a68fdf0

Please sign in to comment.