Skip to content

Commit a68fdf0

Browse files
authored
Support that servers can register a text content provider for multiple schemes (#1539)
1 parent f8c8b09 commit a68fdf0

File tree

5 files changed

+65
-14
lines changed

5 files changed

+65
-14
lines changed

client-node-tests/src/integration.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ suite('Client integration', () => {
318318
willDelete: { filters: [{ scheme: fsProvider.scheme, pattern: { glob: '**/deleted-static/**{/,/*.txt}' } }] },
319319
},
320320
textDocumentContent: {
321-
scheme: 'content-test'
321+
schemes: ['content-test']
322322
}
323323
},
324324
linkedEditingRangeProvider: true,

client-node-tests/src/servers/testServer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ connection.onInitialize((params: InitializeParams): any => {
145145
},
146146
},
147147
textDocumentContent: {
148-
scheme: 'content-test'
148+
schemes: ['content-test']
149149
}
150150
},
151151
linkedEditingRangeProvider: true,

client/src/common/textDocumentContent.ts

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
* ------------------------------------------------------------------------------------------ */
55

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

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

1212

@@ -19,14 +19,35 @@ export interface TextDocumentContentMiddleware {
1919
}
2020

2121
export interface TextDocumentContentProviderShape {
22-
provider: vscode.TextDocumentContentProvider;
22+
scheme: string;
2323
onDidChangeEmitter: vscode.EventEmitter<vscode.Uri>;
24+
provider: vscode.TextDocumentContentProvider;
2425
}
2526

26-
export class TextDocumentContentFeature extends WorkspaceFeature<TextDocumentContentRegistrationOptions, TextDocumentContentProviderShape, TextDocumentContentMiddleware> {
27+
export class TextDocumentContentFeature implements DynamicFeature<TextDocumentContentRegistrationOptions> {
28+
29+
private readonly _client: FeatureClient<TextDocumentContentMiddleware>;
30+
private readonly _registrations: Map<string, { disposable: vscode.Disposable; providers: TextDocumentContentProviderShape[] }> = new Map();
2731

2832
constructor(client: FeatureClient<TextDocumentContentMiddleware>) {
29-
super(client, TextDocumentContentRequest.type);
33+
this._client = client;
34+
}
35+
36+
public getState(): FeatureState {
37+
const registrations = this._registrations.size > 0;
38+
return { kind: 'workspace', id: TextDocumentContentRequest.method, registrations };
39+
}
40+
41+
public get registrationType(): RegistrationType<TextDocumentContentRegistrationOptions> {
42+
return TextDocumentContentRequest.type;
43+
}
44+
45+
public getProviders(): TextDocumentContentProviderShape[] {
46+
const result: TextDocumentContentProviderShape[] = [];
47+
for (const registration of this._registrations.values()) {
48+
result.push(...registration.providers);
49+
}
50+
return result;
3051
}
3152

3253
public fillClientCapabilities(capabilities: ClientCapabilities): void {
@@ -38,8 +59,12 @@ export class TextDocumentContentFeature extends WorkspaceFeature<TextDocumentCon
3859
const client = this._client;
3960
client.onRequest(TextDocumentContentRefreshRequest.type, async (params) => {
4061
const uri = client.protocol2CodeConverter.asUri(params.uri);
41-
for (const provider of this.getProviders()) {
42-
provider.onDidChangeEmitter.fire(uri);
62+
for (const registrations of this._registrations.values()) {
63+
for (const provider of registrations.providers) {
64+
if (provider.scheme !== uri.scheme) {
65+
provider.onDidChangeEmitter.fire(uri);
66+
}
67+
}
4368
}
4469
});
4570

@@ -54,7 +79,18 @@ export class TextDocumentContentFeature extends WorkspaceFeature<TextDocumentCon
5479
});
5580
}
5681

57-
protected registerLanguageProvider(options: TextDocumentContentRegistrationOptions): [vscode.Disposable, TextDocumentContentProviderShape] {
82+
public register(data: RegistrationData<TextDocumentContentRegistrationOptions>): void {
83+
const registrations: TextDocumentContentProviderShape[] = [];
84+
const disposables: vscode.Disposable[] = [];
85+
for (const scheme of data.registerOptions.schemes) {
86+
const [disposable, registration] = this.registerTextDocumentContentProvider(scheme);
87+
registrations.push(registration);
88+
disposables.push(disposable);
89+
}
90+
this._registrations.set(data.id, { disposable: vscode.Disposable.from(...disposables), providers: registrations });
91+
}
92+
93+
private registerTextDocumentContentProvider(scheme: string): [vscode.Disposable, TextDocumentContentProviderShape] {
5894
const eventEmitter: vscode.EventEmitter<vscode.Uri> = new vscode.EventEmitter<vscode.Uri>();
5995
const provider: vscode.TextDocumentContentProvider = {
6096
onDidChange: eventEmitter.event,
@@ -79,6 +115,21 @@ export class TextDocumentContentFeature extends WorkspaceFeature<TextDocumentCon
79115
: provideTextDocumentContent(uri, token);
80116
}
81117
};
82-
return [vscode.workspace.registerTextDocumentContentProvider(options.scheme, provider), { provider, onDidChangeEmitter: eventEmitter }];
118+
return [vscode.workspace.registerTextDocumentContentProvider(scheme, provider), { scheme, onDidChangeEmitter: eventEmitter, provider }];
119+
}
120+
121+
public unregister(id: string): void {
122+
const registration = this._registrations.get(id);
123+
if (registration !== undefined) {
124+
this._registrations.delete(id);
125+
registration.disposable.dispose();
126+
}
127+
}
128+
129+
public clear(): void {
130+
this._registrations.forEach((registration) => {
131+
registration.disposable.dispose();
132+
});
133+
this._registrations.clear();
83134
}
84135
}

protocol/src/common/protocol.textDocumentContent.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ export type TextDocumentContentClientCapabilities = {
3030
*/
3131
export type TextDocumentContentOptions = {
3232
/**
33-
* The scheme for which the server provides content.
33+
* The schemes for which the server provides content.
3434
*/
35-
scheme: string;
35+
schemes: string[];
3636
};
3737

3838
/**

testbed/server/src/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ connection.onInitialize((params, cancel, progress): Thenable<InitializeResult> |
158158
changeNotifications: true
159159
},
160160
textDocumentContent: {
161-
scheme: 'test-content'
161+
schemes: ['test-content']
162162
}
163163
},
164164
implementationProvider: {

0 commit comments

Comments
 (0)