From e127b3e88790822659d088679f18eb4e40bf9d3b Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Mon, 5 Aug 2024 18:19:32 +0200 Subject: [PATCH 1/3] WIP --- client/src/common/client.ts | 13 ++-- client/src/common/textDocumentContent.ts | 69 ++++++++++++++++++ .../common/protocol.textDocumentContent.ts | 71 +++++++++++++++++++ protocol/src/common/protocol.ts | 26 ++++++- server/src/common/textDocumentContent.ts | 5 ++ server/src/common/workspaceFolder.ts | 1 - 6 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 client/src/common/textDocumentContent.ts create mode 100644 protocol/src/common/protocol.textDocumentContent.ts create mode 100644 server/src/common/textDocumentContent.ts diff --git a/client/src/common/client.ts b/client/src/common/client.ts index ffff2553..845fbc64 100644 --- a/client/src/common/client.ts +++ b/client/src/common/client.ts @@ -12,7 +12,8 @@ import { OnTypeFormattingEditProvider, RenameProvider, DocumentSymbolProvider, DocumentLinkProvider, DeclarationProvider, ImplementationProvider, DocumentColorProvider, SelectionRangeProvider, TypeDefinitionProvider, CallHierarchyProvider, LinkedEditingRangeProvider, TypeHierarchyProvider, WorkspaceSymbolProvider, ProviderResult, TextEdit as VTextEdit, InlineCompletionItemProvider, EventEmitter, type TabChangeEvent, TabInputText, TabInputTextDiff, TabInputCustom, - TabInputNotebook + TabInputNotebook, + type TextDocumentContentProvider } from 'vscode'; import { @@ -38,7 +39,7 @@ import { ConnectionOptions, PositionEncodingKind, DocumentDiagnosticRequest, NotebookDocumentSyncRegistrationType, NotebookDocumentSyncRegistrationOptions, ErrorCodes, MessageStrategy, DidOpenTextDocumentParams, CodeLensResolveRequest, CompletionResolveRequest, CodeActionResolveRequest, InlayHintResolveRequest, DocumentLinkResolveRequest, WorkspaceSymbolResolveRequest, CancellationToken as ProtocolCancellationToken, InlineCompletionRequest, InlineCompletionRegistrationOptions, ExecuteCommandRequest, ExecuteCommandOptions, HandlerResult, - type DidCloseTextDocumentParams + type DidCloseTextDocumentParams, type TextDocumentContentRequest } from 'vscode-languageserver-protocol'; import * as c2p from './codeConverter'; @@ -94,6 +95,7 @@ import { InlayHintsFeature, InlayHintsMiddleware, InlayHintsProviderShape } from import { WorkspaceFoldersFeature, WorkspaceFolderMiddleware } from './workspaceFolder'; import { DidCreateFilesFeature, DidDeleteFilesFeature, DidRenameFilesFeature, WillCreateFilesFeature, WillDeleteFilesFeature, WillRenameFilesFeature, FileOperationsMiddleware } from './fileOperations'; import { InlineCompletionItemFeature, InlineCompletionMiddleware } from './inlineCompletion'; +import { TextDocumentContentFeature, type TextDocumentContentMiddleware } from './textDocumentContent'; import { FileSystemWatcherFeature } from './fileSystemWatcher'; import { ProgressFeature } from './progress'; @@ -342,7 +344,8 @@ export type Middleware = _Middleware & TextDocumentSynchronizationMiddleware & C DocumentHighlightMiddleware & DocumentSymbolMiddleware & WorkspaceSymbolMiddleware & ReferencesMiddleware & TypeDefinitionMiddleware & ImplementationMiddleware & ColorProviderMiddleware & CodeActionMiddleware & CodeLensMiddleware & FormattingMiddleware & RenameMiddleware & DocumentLinkMiddleware & ExecuteCommandMiddleware & FoldingRangeProviderMiddleware & DeclarationMiddleware & SelectionRangeProviderMiddleware & CallHierarchyMiddleware & SemanticTokensMiddleware & -LinkedEditingRangeMiddleware & TypeHierarchyMiddleware & InlineValueMiddleware & InlayHintsMiddleware & NotebookDocumentMiddleware & DiagnosticProviderMiddleware & InlineCompletionMiddleware & GeneralMiddleware; +LinkedEditingRangeMiddleware & TypeHierarchyMiddleware & InlineValueMiddleware & InlayHintsMiddleware & NotebookDocumentMiddleware & DiagnosticProviderMiddleware & +InlineCompletionMiddleware & TextDocumentContentMiddleware & GeneralMiddleware; export type LanguageClientOptions = { documentSelector?: DocumentSelector | string[]; @@ -1933,6 +1936,7 @@ export abstract class BaseLanguageClient implements FeatureClient & TextDocumentProviderFeature & DiagnosticFeatureShape; getFeature(request: typeof NotebookDocumentSyncRegistrationType.method): DynamicFeature & NotebookDocumentProviderShape; getFeature(request: typeof InlineCompletionRequest.method): (DynamicFeature & TextDocumentProviderFeature) | undefined; + getFeature(request: typeof TextDocumentContentRequest.method): DynamicFeature & WorkspaceProviderFeature | undefined; getFeature(request: typeof ExecuteCommandRequest.method): DynamicFeature; public getFeature(request: string): DynamicFeature | undefined { return this._dynamicFeatures.get(request); @@ -2462,7 +2466,8 @@ function createConnection(input: MessageReader, output: MessageWriter, errorHand export namespace ProposedFeatures { export function createAll(_client: FeatureClient): (StaticFeature | DynamicFeature)[] { const result: (StaticFeature | DynamicFeature)[] = [ - new InlineCompletionItemFeature(_client) + new InlineCompletionItemFeature(_client), + new TextDocumentContentFeature(_client) ]; return result; } diff --git a/client/src/common/textDocumentContent.ts b/client/src/common/textDocumentContent.ts new file mode 100644 index 00000000..80eee8d6 --- /dev/null +++ b/client/src/common/textDocumentContent.ts @@ -0,0 +1,69 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import * as vscode from 'vscode'; +import { StaticRegistrationOptions, TextDocumentContentRequest, type ClientCapabilities, type ServerCapabilities, type TextDocumentContentParams, type TextDocumentContentRegistrationOptions } from 'vscode-languageserver-protocol'; + +import { WorkspaceFeature, ensure, type FeatureClient } from './features'; +import * as UUID from './utils/uuid'; + + +export interface ProvideTextDocumentContentSignature { + (this: void, uri: vscode.Uri, token: vscode.CancellationToken): vscode.ProviderResult; +} + +export interface TextDocumentContentMiddleware { + provideTextDocumentContent?: (this: void, uri: vscode.Uri, token: vscode.CancellationToken, next: ProvideTextDocumentContentSignature) => vscode.ProviderResult; +} + +export class TextDocumentContentFeature extends WorkspaceFeature { + + constructor(client: FeatureClient) { + super(client, TextDocumentContentRequest.type); + } + + public fillClientCapabilities(capabilities: ClientCapabilities): void { + const textDocumentContent = ensure(ensure(capabilities, 'workspace')!, 'textDocumentContent')!; + textDocumentContent.dynamicRegistration = true; + } + + public initialize(capabilities: ServerCapabilities): void { + if (!capabilities?.workspace?.textDocumentContent) { + return; + } + const capability = capabilities.workspace.textDocumentContent; + const id = StaticRegistrationOptions.hasId(capability) ? capability.id : UUID.generateUuid(); + this.register({ + id: id, + registerOptions: capability + }); + } + + protected registerLanguageProvider(options: TextDocumentContentRegistrationOptions): [vscode.Disposable, vscode.TextDocumentContentProvider] { + const provider: vscode.TextDocumentContentProvider = { + provideTextDocumentContent: (uri, token) => { + const client = this._client; + const provideTextDocumentContent: ProvideTextDocumentContentSignature = (uri, token) => { + const params: TextDocumentContentParams = { + uri: client.code2ProtocolConverter.asUri(uri) + }; + return client.sendRequest(TextDocumentContentRequest.type, params, token).then((result) => { + if (token.isCancellationRequested) { + return null; + } + return result; + }, (error) => { + return client.handleFailedRequest(TextDocumentContentRequest.type, token, error, null); + }); + }; + const middleware = client.middleware; + return middleware.provideTextDocumentContent + ? middleware.provideTextDocumentContent(uri, token, provideTextDocumentContent) + : provideTextDocumentContent(uri, token); + } + }; + return [vscode.workspace.registerTextDocumentContentProvider(options.scheme, provider), provider]; + } +} \ No newline at end of file diff --git a/protocol/src/common/protocol.textDocumentContent.ts b/protocol/src/common/protocol.textDocumentContent.ts new file mode 100644 index 00000000..b1a44ee5 --- /dev/null +++ b/protocol/src/common/protocol.textDocumentContent.ts @@ -0,0 +1,71 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import type { DocumentUri } from 'vscode-languageserver-types'; +import type { RequestHandler } from 'vscode-jsonrpc'; + +import { MessageDirection, ProtocolRequestType } from './messages'; +import type { StaticRegistrationOptions } from './protocol'; + +/** + * Client capabilities for a text document content provider. + * + * @since 3.18.0 + * @proposed + */ +export type TextDocumentContentClientCapabilities = { + /** + * Text document content provider supports dynamic registration. + */ + dynamicRegistration?: boolean; +}; + +/** + * Text document content provider options. + * + * @since 3.18.0 + * @proposed + */ +export type TextDocumentContentOptions = { + /** + * The scheme for which the server provides content. + */ + scheme: string; +}; + +/** + * Text document content provider registration options. + * + * @since 3.18.0 + * @proposed + */ +export type TextDocumentContentRegistrationOptions = TextDocumentContentOptions & StaticRegistrationOptions; + +/** + * Parameters for the `workspace/textDocumentContent` request. + * + * @since 3.18.0 + * @proposed + */ +export interface TextDocumentContentParams { + /** + * The uri of the text document. + */ + uri: DocumentUri; +} + +/** + * The `workspace/textDocumentContent` request is sent from the client to the + * server to request the content of a text document. + * + * @since 3.18.0 + * @proposed + */ +export namespace TextDocumentContentRequest { + export const method: 'workspace/textDocumentContent' = 'workspace/textDocumentContent'; + export const messageDirection: MessageDirection = MessageDirection.clientToServer; + export const type = new ProtocolRequestType(method); + export type HandlerSignature = RequestHandler; +} \ No newline at end of file diff --git a/protocol/src/common/protocol.ts b/protocol/src/common/protocol.ts index 2bbb7a04..f31fc005 100644 --- a/protocol/src/common/protocol.ts +++ b/protocol/src/common/protocol.ts @@ -121,7 +121,13 @@ import { DidCloseNotebookDocumentNotification, NotebookDocumentFilterWithCells, NotebookDocumentFilterWithNotebook } from './protocol.notebook'; -import { InlineCompletionClientCapabilities, InlineCompletionOptions, InlineCompletionParams, InlineCompletionRegistrationOptions, InlineCompletionRequest } from './protocol.inlineCompletion'; +import { + InlineCompletionClientCapabilities, InlineCompletionOptions, InlineCompletionParams, InlineCompletionRegistrationOptions, InlineCompletionRequest +} from './protocol.inlineCompletion'; + +import { + TextDocumentContentClientCapabilities, TextDocumentContentOptions, TextDocumentContentRegistrationOptions, TextDocumentContentParams, TextDocumentContentRequest +} from './protocol.textDocumentContent'; // @ts-ignore: to avoid inlining LocationLink as dynamic import let __noDynamicImport: LocationLink | undefined; @@ -654,6 +660,14 @@ export interface WorkspaceClientCapabilities { * @proposed */ foldingRange?: FoldingRangeWorkspaceClientCapabilities; + + /** + * Capabilities specific to the `workspace/textDocumentContent` request. + * + * @since 3.18.0 + * @proposed + */ + textDocumentContent?: TextDocumentContentClientCapabilities; } /** @@ -1186,6 +1200,14 @@ export type WorkspaceOptions = { * @since 3.16.0 */ fileOperations?: FileOperationOptions; + + /** + * The server supports the `workspace/textDocumentContent` request. + * + * @since 3.18.0 + * @proposed + */ + textDocumentContent?: TextDocumentContentOptions | TextDocumentContentRegistrationOptions; }; /** @@ -4286,6 +4308,8 @@ export { DidCloseNotebookDocumentNotification, NotebookDocumentFilterWithCells, NotebookDocumentFilterWithNotebook, // Inline Completions InlineCompletionClientCapabilities, InlineCompletionOptions, InlineCompletionParams, InlineCompletionRegistrationOptions, InlineCompletionRequest, + // Text Document Content + TextDocumentContentClientCapabilities, TextDocumentContentOptions, TextDocumentContentRegistrationOptions, TextDocumentContentParams, TextDocumentContentRequest }; // To be backwards compatible diff --git a/server/src/common/textDocumentContent.ts b/server/src/common/textDocumentContent.ts new file mode 100644 index 00000000..28ebe68c --- /dev/null +++ b/server/src/common/textDocumentContent.ts @@ -0,0 +1,5 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + diff --git a/server/src/common/workspaceFolder.ts b/server/src/common/workspaceFolder.ts index 0170bc04..b3a3b345 100644 --- a/server/src/common/workspaceFolder.ts +++ b/server/src/common/workspaceFolder.ts @@ -11,7 +11,6 @@ import { import type { Feature, _RemoteWorkspace } from './server'; - export interface WorkspaceFolders { getWorkspaceFolders(): Promise; onDidChangeWorkspaceFolders: Event; From 54baeb9d781efc899c583d678131e724ba36cc35 Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Tue, 6 Aug 2024 17:46:47 +0200 Subject: [PATCH 2/3] WIP --- client-node-tests/src/integration.test.ts | 21 ++++++++++++++ client-node-tests/src/servers/testServer.ts | 7 +++++ client/src/common/client.ts | 7 ++--- client/src/common/textDocumentContent.ts | 23 ++++++++++++--- .../common/protocol.textDocumentContent.ts | 27 +++++++++++++++++ protocol/src/common/protocol.ts | 6 ++-- server/src/common/api.ts | 6 ++-- server/src/common/textDocumentContent.ts | 29 +++++++++++++++++++ 8 files changed, 114 insertions(+), 12 deletions(-) diff --git a/client-node-tests/src/integration.test.ts b/client-node-tests/src/integration.test.ts index 3b18c877..dcac1430 100644 --- a/client-node-tests/src/integration.test.ts +++ b/client-node-tests/src/integration.test.ts @@ -317,6 +317,9 @@ suite('Client integration', () => { }, willDelete: { filters: [{ scheme: fsProvider.scheme, pattern: { glob: '**/deleted-static/**{/,/*.txt}' } }] }, }, + textDocumentContent: { + scheme: 'content-test' + } }, linkedEditingRangeProvider: true, diagnosticProvider: { @@ -1536,6 +1539,24 @@ suite('Client integration', () => { rangeEqual(symbol.location.range, 1, 2, 3, 4); }); + test('Text Document Content', async () => { + const providers = client.getFeature(lsclient.TextDocumentContentRequest.method)?.getProviders(); + isDefined(providers); + assert.strictEqual(providers.length, 1); + const provider = providers[0].provider; + const result = await provider.provideTextDocumentContent(vscode.Uri.parse('content-test:///test.txt'), tokenSource.token); + assert.strictEqual(result, 'Some test content'); + + let middlewareCalled: boolean = false; + middleware.provideTextDocumentContent = (uri, token, next) => { + middlewareCalled = true; + return next(uri, token); + }; + await provider.provideTextDocumentContent(vscode.Uri.parse('content-test:///test.txt'), tokenSource.token); + middleware.provideTextDocumentContent = undefined; + assert.strictEqual(middlewareCalled, true); + }); + test('General middleware', async () => { let middlewareCallCount = 0; diff --git a/client-node-tests/src/servers/testServer.ts b/client-node-tests/src/servers/testServer.ts index 0423d356..cd94a9f7 100644 --- a/client-node-tests/src/servers/testServer.ts +++ b/client-node-tests/src/servers/testServer.ts @@ -144,6 +144,9 @@ connection.onInitialize((params: InitializeParams): any => { filters: [{ scheme: 'file-test', pattern: { glob: '**/deleted-static/**{/,/*.txt}' } }] }, }, + textDocumentContent: { + scheme: 'content-test' + } }, linkedEditingRangeProvider: true, diagnosticProvider: { @@ -522,6 +525,10 @@ connection.languages.inlineCompletion.on((_params) => { ]; }); +connection.workspace.textDocumentContent.on((_params) => { + return 'Some test content'; +}); + connection.onRequest( new ProtocolRequestType('testing/sendSampleProgress'), async (_, __) => { diff --git a/client/src/common/client.ts b/client/src/common/client.ts index 845fbc64..5540cedd 100644 --- a/client/src/common/client.ts +++ b/client/src/common/client.ts @@ -12,8 +12,7 @@ import { OnTypeFormattingEditProvider, RenameProvider, DocumentSymbolProvider, DocumentLinkProvider, DeclarationProvider, ImplementationProvider, DocumentColorProvider, SelectionRangeProvider, TypeDefinitionProvider, CallHierarchyProvider, LinkedEditingRangeProvider, TypeHierarchyProvider, WorkspaceSymbolProvider, ProviderResult, TextEdit as VTextEdit, InlineCompletionItemProvider, EventEmitter, type TabChangeEvent, TabInputText, TabInputTextDiff, TabInputCustom, - TabInputNotebook, - type TextDocumentContentProvider + TabInputNotebook } from 'vscode'; import { @@ -95,7 +94,7 @@ import { InlayHintsFeature, InlayHintsMiddleware, InlayHintsProviderShape } from import { WorkspaceFoldersFeature, WorkspaceFolderMiddleware } from './workspaceFolder'; import { DidCreateFilesFeature, DidDeleteFilesFeature, DidRenameFilesFeature, WillCreateFilesFeature, WillDeleteFilesFeature, WillRenameFilesFeature, FileOperationsMiddleware } from './fileOperations'; import { InlineCompletionItemFeature, InlineCompletionMiddleware } from './inlineCompletion'; -import { TextDocumentContentFeature, type TextDocumentContentMiddleware } from './textDocumentContent'; +import { TextDocumentContentFeature, type TextDocumentContentMiddleware, type TextDocumentContentProviderShape } from './textDocumentContent'; import { FileSystemWatcherFeature } from './fileSystemWatcher'; import { ProgressFeature } from './progress'; @@ -1936,7 +1935,7 @@ export abstract class BaseLanguageClient implements FeatureClient & TextDocumentProviderFeature & DiagnosticFeatureShape; getFeature(request: typeof NotebookDocumentSyncRegistrationType.method): DynamicFeature & NotebookDocumentProviderShape; getFeature(request: typeof InlineCompletionRequest.method): (DynamicFeature & TextDocumentProviderFeature) | undefined; - getFeature(request: typeof TextDocumentContentRequest.method): DynamicFeature & WorkspaceProviderFeature | undefined; + getFeature(request: typeof TextDocumentContentRequest.method): DynamicFeature & WorkspaceProviderFeature | undefined; getFeature(request: typeof ExecuteCommandRequest.method): DynamicFeature; public getFeature(request: string): DynamicFeature | undefined { return this._dynamicFeatures.get(request); diff --git a/client/src/common/textDocumentContent.ts b/client/src/common/textDocumentContent.ts index 80eee8d6..3c7124df 100644 --- a/client/src/common/textDocumentContent.ts +++ b/client/src/common/textDocumentContent.ts @@ -4,7 +4,7 @@ * ------------------------------------------------------------------------------------------ */ import * as vscode from 'vscode'; -import { StaticRegistrationOptions, TextDocumentContentRequest, type ClientCapabilities, type ServerCapabilities, type TextDocumentContentParams, type TextDocumentContentRegistrationOptions } from 'vscode-languageserver-protocol'; +import { StaticRegistrationOptions, TextDocumentContentRefreshRequest, TextDocumentContentRequest, type ClientCapabilities, type ServerCapabilities, type TextDocumentContentParams, type TextDocumentContentRegistrationOptions } from 'vscode-languageserver-protocol'; import { WorkspaceFeature, ensure, type FeatureClient } from './features'; import * as UUID from './utils/uuid'; @@ -18,7 +18,12 @@ export interface TextDocumentContentMiddleware { provideTextDocumentContent?: (this: void, uri: vscode.Uri, token: vscode.CancellationToken, next: ProvideTextDocumentContentSignature) => vscode.ProviderResult; } -export class TextDocumentContentFeature extends WorkspaceFeature { +export interface TextDocumentContentProviderShape { + provider: vscode.TextDocumentContentProvider; + onDidChangeEmitter: vscode.EventEmitter; +} + +export class TextDocumentContentFeature extends WorkspaceFeature { constructor(client: FeatureClient) { super(client, TextDocumentContentRequest.type); @@ -30,6 +35,14 @@ export class TextDocumentContentFeature extends WorkspaceFeature { + const uri = client.protocol2CodeConverter.asUri(params.uri); + for (const provider of this.getProviders()) { + provider.onDidChangeEmitter.fire(uri); + } + }); + if (!capabilities?.workspace?.textDocumentContent) { return; } @@ -41,8 +54,10 @@ export class TextDocumentContentFeature extends WorkspaceFeature = new vscode.EventEmitter(); const provider: vscode.TextDocumentContentProvider = { + onDidChange: eventEmitter.event, provideTextDocumentContent: (uri, token) => { const client = this._client; const provideTextDocumentContent: ProvideTextDocumentContentSignature = (uri, token) => { @@ -64,6 +79,6 @@ export class TextDocumentContentFeature extends WorkspaceFeature(method); export type HandlerSignature = RequestHandler; +} + +/** + * Parameters for the `workspace/textDocumentContent/refresh` request. + * + * @since 3.18.0 + * @proposed + */ +export interface TextDocumentContentRefreshParams { + /** + * The uri of the text document to refresh. + */ + uri: DocumentUri; +} + +/** + * The `workspace/textDocumentContent` request is sent from the server to the client to refresh + * the content of a specific text document. + * + * @since 3.18.0 + * @proposed + */ +export namespace TextDocumentContentRefreshRequest { + export const method: `workspace/textDocumentContent/refresh` = `workspace/textDocumentContent/refresh`; + export const messageDirection: MessageDirection = MessageDirection.serverToClient; + export const type = new ProtocolRequestType(method); + export type HandlerSignature = RequestHandler; } \ No newline at end of file diff --git a/protocol/src/common/protocol.ts b/protocol/src/common/protocol.ts index f31fc005..e1a3e160 100644 --- a/protocol/src/common/protocol.ts +++ b/protocol/src/common/protocol.ts @@ -126,7 +126,8 @@ import { } from './protocol.inlineCompletion'; import { - TextDocumentContentClientCapabilities, TextDocumentContentOptions, TextDocumentContentRegistrationOptions, TextDocumentContentParams, TextDocumentContentRequest + TextDocumentContentClientCapabilities, TextDocumentContentOptions, TextDocumentContentRegistrationOptions, TextDocumentContentParams, + TextDocumentContentRequest, TextDocumentContentRefreshParams, TextDocumentContentRefreshRequest } from './protocol.textDocumentContent'; // @ts-ignore: to avoid inlining LocationLink as dynamic import @@ -4309,7 +4310,8 @@ export { // Inline Completions InlineCompletionClientCapabilities, InlineCompletionOptions, InlineCompletionParams, InlineCompletionRegistrationOptions, InlineCompletionRequest, // Text Document Content - TextDocumentContentClientCapabilities, TextDocumentContentOptions, TextDocumentContentRegistrationOptions, TextDocumentContentParams, TextDocumentContentRequest + TextDocumentContentClientCapabilities, TextDocumentContentOptions, TextDocumentContentRegistrationOptions, TextDocumentContentParams, TextDocumentContentRequest, + TextDocumentContentRefreshParams, TextDocumentContentRefreshRequest }; // To be backwards compatible diff --git a/server/src/common/api.ts b/server/src/common/api.ts index ddb01e24..5bf884de 100644 --- a/server/src/common/api.ts +++ b/server/src/common/api.ts @@ -8,6 +8,7 @@ import { SemanticTokensBuilder } from './semanticTokens'; import type { WorkDoneProgressReporter, WorkDoneProgressServerReporter, ResultProgressReporter } from './progress'; import * as ic from './inlineCompletion.proposed'; +import * as tdc from './textDocumentContent'; export * from 'vscode-languageserver-protocol'; export { WorkDoneProgressReporter, WorkDoneProgressServerReporter, ResultProgressReporter }; @@ -19,10 +20,11 @@ export { NotebookDocuments }; export * from './server'; export namespace ProposedFeatures { - export const all: Features<_, _, _, _, _, _, ic.InlineCompletionFeatureShape, _> = { + export const all: Features<_, _, _, _, _, tdc.TextDocumentContentFeatureShape, ic.InlineCompletionFeatureShape, _> = { __brand: 'features', + workspace: tdc.TextDocumentContentFeature, languages: ic.InlineCompletionFeature }; - export type Connection = _Connection<_, _, _, _, _, _, ic.InlineCompletionFeatureShape, _>; + export type Connection = _Connection<_, _, _, _, _, tdc.TextDocumentContentFeatureShape, ic.InlineCompletionFeatureShape, _>; } \ No newline at end of file diff --git a/server/src/common/textDocumentContent.ts b/server/src/common/textDocumentContent.ts index 28ebe68c..cf15c40d 100644 --- a/server/src/common/textDocumentContent.ts +++ b/server/src/common/textDocumentContent.ts @@ -3,3 +3,32 @@ * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ +import { TextDocumentContentRequest, type Disposable, type RequestHandler, type TextDocumentContentParams } from 'vscode-languageserver-protocol'; + +import type { Feature, _RemoteWorkspace } from './server'; + +/** + * Shape of the text document content feature + * + * @since 3.18.0 + * @proposed + */ +export interface TextDocumentContentFeatureShape { + textDocumentContent: { + on(handler: RequestHandler): Disposable; + }; +} + +export const TextDocumentContentFeature: Feature<_RemoteWorkspace, TextDocumentContentFeatureShape> = (Base) => { + return class extends Base { + public get textDocumentContent() { + return { + on: (handler: RequestHandler): Disposable => { + return this.connection.onRequest(TextDocumentContentRequest.type, (params, cancel) => { + return handler(params, cancel); + }); + } + }; + } + }; +}; \ No newline at end of file From 8b5d973b047452b1be88d5377190b7ff1310a965 Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Wed, 7 Aug 2024 11:41:34 +0200 Subject: [PATCH 3/3] Add refresh support and test bed code. --- server/src/common/textDocumentContent.ts | 6 +++++- testbed/client/src/extension.ts | 12 +++++++++++- testbed/package.json | 8 ++++++++ testbed/server/src/server.ts | 20 ++++++++++++++++++-- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/server/src/common/textDocumentContent.ts b/server/src/common/textDocumentContent.ts index cf15c40d..c1d5794c 100644 --- a/server/src/common/textDocumentContent.ts +++ b/server/src/common/textDocumentContent.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ -import { TextDocumentContentRequest, type Disposable, type RequestHandler, type TextDocumentContentParams } from 'vscode-languageserver-protocol'; +import { TextDocumentContentRefreshRequest, TextDocumentContentRequest, type Disposable, type DocumentUri, type RequestHandler, type TextDocumentContentParams } from 'vscode-languageserver-protocol'; import type { Feature, _RemoteWorkspace } from './server'; @@ -15,6 +15,7 @@ import type { Feature, _RemoteWorkspace } from './server'; */ export interface TextDocumentContentFeatureShape { textDocumentContent: { + refresh(uri: DocumentUri): Promise; on(handler: RequestHandler): Disposable; }; } @@ -23,6 +24,9 @@ export const TextDocumentContentFeature: Feature<_RemoteWorkspace, TextDocumentC return class extends Base { public get textDocumentContent() { return { + refresh: (uri: DocumentUri): Promise => { + return this.connection.sendRequest(TextDocumentContentRefreshRequest.type, { uri }); + }, on: (handler: RequestHandler): Disposable => { return this.connection.onRequest(TextDocumentContentRequest.type, (params, cancel) => { return handler(params, cancel); diff --git a/testbed/client/src/extension.ts b/testbed/client/src/extension.ts index 1f5519bd..89575fd5 100644 --- a/testbed/client/src/extension.ts +++ b/testbed/client/src/extension.ts @@ -5,7 +5,7 @@ 'use strict'; import * as path from 'path'; -import { commands, ExtensionContext, workspace, window } from 'vscode'; +import { commands, ExtensionContext, workspace, window, Uri } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, NotificationType } from 'vscode-languageclient/node'; let client: LanguageClient; @@ -106,6 +106,16 @@ REM or .bmp extension from c:\\source to c:\\images;;`; await window.showTextDocument(doc); }); }); + + commands.registerCommand('testbed.fileWithContent', async () => { + const doc = await workspace.openTextDocument(Uri.parse('test-content://file.txt')); + await window.showTextDocument(doc); + }); + + const refreshNotification: NotificationType = new NotificationType('testbed/refreshContent'); + commands.registerCommand('testbed.refreshContent', async () => { + await client.sendNotification(refreshNotification, 'test-content://file.txt'); + }); } export function deactivate() { diff --git a/testbed/package.json b/testbed/package.json index bb2b3abd..b167a931 100644 --- a/testbed/package.json +++ b/testbed/package.json @@ -17,6 +17,14 @@ { "command": "testbed.openFile", "title": "Open Test File" + }, + { + "command": "testbed.fileWithContent", + "title": "Open file with dynamic content" + }, + { + "command": "testbed.refreshContent", + "title": "Refresh dynamic content" } ], "configuration": { diff --git a/testbed/server/src/server.ts b/testbed/server/src/server.ts index d9690fbd..451b6a71 100644 --- a/testbed/server/src/server.ts +++ b/testbed/server/src/server.ts @@ -17,7 +17,8 @@ import { TextEdit, ProposedFeatures, InsertTextFormat, SelectionRangeRequest, SelectionRange, InsertReplaceEdit, SemanticTokensClientCapabilities, SemanticTokensLegend, SemanticTokensBuilder, SemanticTokensRegistrationType, SemanticTokensRegistrationOptions, ProtocolNotificationType, ChangeAnnotation, WorkspaceChange, CompletionItemKind, DiagnosticSeverity, - DocumentDiagnosticReportKind, WorkspaceDiagnosticReport, NotebookDocuments, CompletionList, DidChangeConfigurationNotification + DocumentDiagnosticReportKind, WorkspaceDiagnosticReport, NotebookDocuments, CompletionList, DidChangeConfigurationNotification, + NotificationType } from 'vscode-languageserver/node'; import { @@ -155,6 +156,9 @@ connection.onInitialize((params, cancel, progress): Thenable | workspaceFolders: { supported: true, changeNotifications: true + }, + textDocumentContent: { + scheme: 'test-content' } }, implementationProvider: { @@ -177,7 +181,8 @@ connection.onInitialize((params, cancel, progress): Thenable | notebookSelector: [{ cells: [{ language: 'bat'}] }] - } + }, + } }; setTimeout(() => { @@ -692,6 +697,17 @@ connection.languages.semanticTokens.onRange((params) => { return { data: [] }; }); +let counter = 0; +connection.workspace.textDocumentContent.on((_param) => { + return `Text content version ${counter++}`; +}); + +const refreshNotification: NotificationType = new NotificationType('testbed/refreshContent'); +connection.onNotification(refreshNotification, async (uri) => { + await connection.workspace.textDocumentContent.refresh(uri); +}); + + const notebooks = new NotebookDocuments(TextDocument); notebooks.onDidOpen(() => { connection.console.log(`Notebook opened`);