diff --git a/client-node-tests/package-lock.json b/client-node-tests/package-lock.json index dff06e06c..191a22f62 100644 --- a/client-node-tests/package-lock.json +++ b/client-node-tests/package-lock.json @@ -17,7 +17,7 @@ "@types/minimatch": "^3.0.5", "@types/sinon": "^10.0.2", "@types/uuid": "^8.3.1", - "@types/vscode": "1.67.0", + "@types/vscode": "1.68.0", "find-process": "^1.4.7", "glob": "^7.1.7", "sinon": "^11.1.2", @@ -25,7 +25,7 @@ "vscode-test": "^1.6.1" }, "engines": { - "vscode": "^1.67.0" + "vscode": "^1.68.0" } }, "node_modules/@sinonjs/commons": { @@ -110,9 +110,9 @@ "dev": true }, "node_modules/@types/vscode": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.67.0.tgz", - "integrity": "sha512-GH8BDf8cw9AC9080uneJfulhSa7KHSMI2s/CyKePXoGNos9J486w2V4YKoeNUqIEkW4hKoEAWp6/cXTwyGj47g==", + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.68.0.tgz", + "integrity": "sha512-duBwEK5ta/eBBMJMQ7ECMEsMvlE3XJdRGh3xoS1uOO4jl2Z4LPBl5vx8WvBP10ERAgDRmIt/FaSD4RHyBGbChw==", "dev": true }, "node_modules/agent-base": { @@ -811,9 +811,9 @@ "dev": true }, "@types/vscode": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.67.0.tgz", - "integrity": "sha512-GH8BDf8cw9AC9080uneJfulhSa7KHSMI2s/CyKePXoGNos9J486w2V4YKoeNUqIEkW4hKoEAWp6/cXTwyGj47g==", + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.68.0.tgz", + "integrity": "sha512-duBwEK5ta/eBBMJMQ7ECMEsMvlE3XJdRGh3xoS1uOO4jl2Z4LPBl5vx8WvBP10ERAgDRmIt/FaSD4RHyBGbChw==", "dev": true }, "agent-base": { diff --git a/client-node-tests/package.json b/client-node-tests/package.json index d92f7ca8d..75173590a 100644 --- a/client-node-tests/package.json +++ b/client-node-tests/package.json @@ -6,7 +6,7 @@ "description": "", "version": "0.0.1", "engines": { - "vscode": "^1.67.0" + "vscode": "^1.68.0" }, "categories": [ "Other" @@ -14,9 +14,6 @@ "activationEvents": [ "*" ], - "enabledApiProposals": [ - "notebookContentProvider" - ], "main": "./extension.js", "contributes": {}, "scripts": { @@ -41,7 +38,7 @@ "@types/minimatch": "^3.0.5", "@types/sinon": "^10.0.2", "@types/uuid": "^8.3.1", - "@types/vscode": "1.67.0", + "@types/vscode": "1.68.0", "find-process": "^1.4.7", "glob": "^7.1.7", "sinon": "^11.1.2", diff --git a/client-node-tests/src/converter.test.ts b/client-node-tests/src/converter.test.ts index a4354d1ff..3a645d59f 100644 --- a/client-node-tests/src/converter.test.ts +++ b/client-node-tests/src/converter.test.ts @@ -1244,6 +1244,26 @@ suite('Protocol Converter', () => { strictEqual((result[1] as ProtocolInlayHint).data, '2'); }); + test('InlineCompletions', async () => { + const items: proto.InlineCompletionItem[] = [ + proto.InlineCompletionItem.create('insert text', 'in', proto.Range.create(1, 2, 6, 7)), + proto.InlineCompletionItem.create('insert text', 'in', proto.Range.create(1, 2, 6, 7), undefined), + proto.InlineCompletionItem.create(proto.StringValue.create('insert text'), 'in', proto.Range.create(1, 2, 6, 7), undefined), + ]; + + const result = await p2c.asInlineCompletionResult(items); + ok(result.every((r) => r instanceof vscode.InlineCompletionItem)); + for (const r of result) { + rangeEqual(r.range!, proto.Range.create(1, 2, 6, 7)); + } + + ok(result[0] instanceof vscode.InlineCompletionItem && result[0].insertText === 'insert text'); + ok(result[0] instanceof vscode.InlineCompletionItem && result[0].filterText === 'in'); + ok(typeof result[0].insertText === 'string'); + ok(typeof result[1].insertText === 'string'); + ok(result[2].insertText instanceof vscode.SnippetString); + }); + test('Bug #361', () => { const item: proto.CompletionItem = { 'label': 'MyLabel', diff --git a/client-node-tests/src/integration.test.ts b/client-node-tests/src/integration.test.ts index 59818806d..962405ca3 100644 --- a/client-node-tests/src/integration.test.ts +++ b/client-node-tests/src/integration.test.ts @@ -273,6 +273,7 @@ suite('Client integration', () => { delta: true } }, + inlineCompletionProvider: {}, workspace: { fileOperations: { didCreate: { filters: [{ scheme: fsProvider.scheme, pattern: { glob: '**/created-static/**{/,/*.txt}' } }] }, @@ -1445,6 +1446,27 @@ suite('Client integration', () => { assert.strictEqual(edit.newText, 'number'); }); + test('Inline Completions', async () => { + const providerData = client.getFeature(lsclient.InlineCompletionRequest.method).getProvider(document); + isDefined(providerData); + const results = (await providerData.provideInlineCompletionItems(document, position, { triggerKind: 1, selectedCompletionInfo: {range, text: 'text'} }, tokenSource.token)) as vscode.InlineCompletionItem[]; + + isArray(results, vscode.InlineCompletionItem, 1); + + rangeEqual(results[0].range!, 1, 2, 3, 4); + assert.strictEqual(results[0].filterText!, 'te'); + assert.strictEqual(results[0].insertText, 'text inline'); + + let middlewareCalled: boolean = false; + middleware.provideInlineCompletionItems = (d, r, c, t, n) => { + middlewareCalled = true; + return n(d, r, c, t); + }; + await providerData.provideInlineCompletionItems(document, position, { triggerKind: 0, selectedCompletionInfo: undefined }, tokenSource.token); + middleware.provideInlineCompletionItems = undefined; + assert.strictEqual(middlewareCalled, true); + }); + test('Workspace symbols', async () => { const providers = client.getFeature(lsclient.WorkspaceSymbolRequest.method).getProviders(); isDefined(providers); diff --git a/client-node-tests/src/servers/testServer.ts b/client-node-tests/src/servers/testServer.ts index 6feb0b56b..958b0d93e 100644 --- a/client-node-tests/src/servers/testServer.ts +++ b/client-node-tests/src/servers/testServer.ts @@ -11,12 +11,12 @@ import { ColorInformation, Color, ColorPresentation, FoldingRange, SelectionRange, SymbolKind, ProtocolRequestType, WorkDoneProgress, InlineValueText, InlineValueVariableLookup, InlineValueEvaluatableExpression, WorkDoneProgressCreateRequest, WillCreateFilesRequest, WillRenameFilesRequest, WillDeleteFilesRequest, DidDeleteFilesNotification, DidRenameFilesNotification, DidCreateFilesNotification, - ProposedFeatures, Diagnostic, DiagnosticSeverity, TypeHierarchyItem, InlayHint, InlayHintLabelPart, InlayHintKind, DocumentDiagnosticReportKind, DocumentSymbol + ProposedFeatures, Diagnostic, DiagnosticSeverity, TypeHierarchyItem, InlayHint, InlayHintLabelPart, InlayHintKind, DocumentDiagnosticReportKind, DocumentSymbol, InlineCompletionItem } from 'vscode-languageserver/node'; import { URI } from 'vscode-uri'; -const connection: ProposedFeatures.Connection = createConnection(); +const connection = createConnection() as ProposedFeatures.Connection; console.log = connection.console.log.bind(connection.console); console.error = connection.console.error.bind(connection.console); @@ -105,6 +105,7 @@ connection.onInitialize((params: InitializeParams): any => { delta: true } }, + inlineCompletionProvider: {}, workspace: { fileOperations: { // Static reg is folders + .txt files with operation kind in the path @@ -513,6 +514,12 @@ connection.languages.inlayHint.resolve((hint) => { return hint; }); +connection.languages.inlineCompletion.on((_params) => { + return [ + InlineCompletionItem.create('text inline', 'te', Range.create(1,2,3,4)) + ]; +}); + connection.onRequest( new ProtocolRequestType('testing/sendSampleProgress'), async (_, __) => { diff --git a/client/package-lock.json b/client/package-lock.json index f5fb06200..611f523ae 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -16,7 +16,7 @@ "devDependencies": { "@types/minimatch": "^3.0.5", "@types/semver": "^7.3.10", - "@types/vscode": "1.67.0", + "@types/vscode": "1.68.0", "shx": "^0.3.4" }, "engines": { @@ -36,9 +36,9 @@ "dev": true }, "node_modules/@types/vscode": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.67.0.tgz", - "integrity": "sha512-GH8BDf8cw9AC9080uneJfulhSa7KHSMI2s/CyKePXoGNos9J486w2V4YKoeNUqIEkW4hKoEAWp6/cXTwyGj47g==", + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.68.0.tgz", + "integrity": "sha512-duBwEK5ta/eBBMJMQ7ECMEsMvlE3XJdRGh3xoS1uOO4jl2Z4LPBl5vx8WvBP10ERAgDRmIt/FaSD4RHyBGbChw==", "dev": true }, "node_modules/balanced-match": { @@ -335,9 +335,9 @@ "dev": true }, "@types/vscode": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.67.0.tgz", - "integrity": "sha512-GH8BDf8cw9AC9080uneJfulhSa7KHSMI2s/CyKePXoGNos9J486w2V4YKoeNUqIEkW4hKoEAWp6/cXTwyGj47g==", + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.68.0.tgz", + "integrity": "sha512-duBwEK5ta/eBBMJMQ7ECMEsMvlE3XJdRGh3xoS1uOO4jl2Z4LPBl5vx8WvBP10ERAgDRmIt/FaSD4RHyBGbChw==", "dev": true }, "balanced-match": { diff --git a/client/package.json b/client/package.json index 13abf76a9..33320531e 100644 --- a/client/package.json +++ b/client/package.json @@ -24,7 +24,7 @@ "devDependencies": { "@types/minimatch": "^3.0.5", "@types/semver": "^7.3.10", - "@types/vscode": "1.67.0", + "@types/vscode": "1.68.0", "shx": "^0.3.4" }, "dependencies": { diff --git a/client/src/common/client.ts b/client/src/common/client.ts index 2d85843bd..2d96380d4 100644 --- a/client/src/common/client.ts +++ b/client/src/common/client.ts @@ -11,7 +11,7 @@ import { DefinitionProvider, ReferenceProvider, DocumentHighlightProvider, CodeActionProvider, DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider, OnTypeFormattingEditProvider, RenameProvider, DocumentSymbolProvider, DocumentLinkProvider, DeclarationProvider, FoldingRangeProvider, ImplementationProvider, DocumentColorProvider, SelectionRangeProvider, TypeDefinitionProvider, CallHierarchyProvider, LinkedEditingRangeProvider, TypeHierarchyProvider, WorkspaceSymbolProvider, - ProviderResult, TextEdit as VTextEdit + ProviderResult, TextEdit as VTextEdit, InlineCompletionItemProvider } from 'vscode'; import { @@ -36,7 +36,7 @@ import { TypeHierarchyPrepareRequest, InlineValueRequest, InlayHintRequest, WorkspaceSymbolRequest, TextDocumentRegistrationOptions, FileOperationRegistrationOptions, ConnectionOptions, PositionEncodingKind, DocumentDiagnosticRequest, NotebookDocumentSyncRegistrationType, NotebookDocumentSyncRegistrationOptions, ErrorCodes, MessageStrategy, DidOpenTextDocumentParams, CodeLensResolveRequest, CompletionResolveRequest, CodeActionResolveRequest, InlayHintResolveRequest, DocumentLinkResolveRequest, WorkspaceSymbolResolveRequest, - CancellationToken as ProtocolCancellationToken + CancellationToken as ProtocolCancellationToken, InlineCompletionRequest, InlineCompletionRegistrationOptions } from 'vscode-languageserver-protocol'; import * as c2p from './codeConverter'; @@ -90,6 +90,7 @@ import { InlineValueMiddleware, InlineValueProviderShape } from './inlineValue'; import { InlayHintsMiddleware, InlayHintsProviderShape } from './inlayHint'; import { WorkspaceFolderMiddleware } from './workspaceFolder'; import { FileOperationsMiddleware } from './fileOperations'; +import { InlineCompletionMiddleware } from './inlineCompletion'; import { FileSystemWatcherFeature } from './fileSystemWatcher'; import { ColorProviderFeature } from './colorProvider'; import { ImplementationFeature } from './implementation'; @@ -106,6 +107,7 @@ import { LinkedEditingFeature } from './linkedEditingRange'; import { TypeHierarchyFeature } from './typeHierarchy'; import { InlineValueFeature } from './inlineValue'; import { InlayHintsFeature } from './inlayHint'; +import { InlineCompletionItemFeature } from './inlineCompletion'; /** * Controls when the output channel is revealed. @@ -346,7 +348,7 @@ 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 & GeneralMiddleware; +LinkedEditingRangeMiddleware & TypeHierarchyMiddleware & InlineValueMiddleware & InlayHintsMiddleware & NotebookDocumentMiddleware & DiagnosticProviderMiddleware & InlineCompletionMiddleware & GeneralMiddleware; export type LanguageClientOptions = { documentSelector?: DocumentSelector | string[]; @@ -1734,6 +1736,7 @@ export abstract class BaseLanguageClient implements FeatureClient & WorkspaceProviderFeature; getFeature(request: typeof DocumentDiagnosticRequest.method): DynamicFeature & TextDocumentProviderFeature | undefined; getFeature(request: typeof NotebookDocumentSyncRegistrationType.method): DynamicFeature & NotebookDocumentProviderShape | undefined; + getFeature(request: typeof InlineCompletionRequest.method): DynamicFeature & TextDocumentProviderFeature; public getFeature(request: string): DynamicFeature | undefined { return this._dynamicFeatures.get(request); } @@ -2213,6 +2216,7 @@ function createConnection(input: MessageReader, output: MessageWriter, errorHand export namespace ProposedFeatures { export function createAll(_client: FeatureClient): (StaticFeature | DynamicFeature)[] { let result: (StaticFeature | DynamicFeature)[] = [ + new InlineCompletionItemFeature(_client) ]; return result; } diff --git a/client/src/common/codeConverter.ts b/client/src/common/codeConverter.ts index 9cd7a1856..f8ae76717 100644 --- a/client/src/common/codeConverter.ts +++ b/client/src/common/codeConverter.ts @@ -139,6 +139,8 @@ export interface Converter { asWorkspaceSymbol(item: code.SymbolInformation): proto.WorkspaceSymbol; asInlayHint(value: code.InlayHint): proto.InlayHint; + + asInlineCompletionParams(document: code.TextDocument, position: code.Position, context: code.InlineCompletionContext): proto.InlineCompletionParams; } export interface URIConverter { @@ -797,6 +799,11 @@ export function createConverter(uriConverter?: URIConverter): Converter { return proto.InlineValueContext.create(context.frameId, asRange(context.stoppedLocation)); } + function asInlineCompletionParams(document: code.TextDocument, position: code.Position, context: code.InlineCompletionContext): proto.InlineCompletionParams { + return {context: proto.InlineCompletionContext.create(context.triggerKind, context.selectedCompletionInfo), + textDocument: asTextDocumentIdentifier(document), position: asPosition(position)}; + } + function asCommand(item: code.Command): proto.Command { let result = proto.Command.create(item.title, item.command); if (item.arguments) { result.arguments = item.arguments; } @@ -982,6 +989,7 @@ export function createConverter(uriConverter?: URIConverter): Converter { asCallHierarchyItem, asTypeHierarchyItem, asInlayHint, - asWorkspaceSymbol + asWorkspaceSymbol, + asInlineCompletionParams }; } diff --git a/client/src/common/inlineCompletion.ts b/client/src/common/inlineCompletion.ts new file mode 100644 index 000000000..9dfd0073d --- /dev/null +++ b/client/src/common/inlineCompletion.ts @@ -0,0 +1,80 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import { + languages as Languages, TextDocument, Disposable, Position as VPosition, InlineCompletionContext as VInlineCompletionContext, + CancellationToken, ProviderResult, InlineCompletionItem as VInlineCompletionItem, InlineCompletionList as VInlineCompletionList, InlineCompletionItemProvider +} from 'vscode'; + +import { + ClientCapabilities, InlineCompletionOptions, InlineCompletionRegistrationOptions, InlineCompletionRequest, + DocumentSelector, ServerCapabilities +} from 'vscode-languageserver-protocol'; + +import { + FeatureClient, ensure, TextDocumentLanguageFeature +} from './features'; + +import * as UUID from './utils/uuid'; + +export interface ProvideInlineCompletionItemsSignature { + (this: void, document: TextDocument, position: VPosition, context: VInlineCompletionContext, token: CancellationToken): ProviderResult; +} + +export interface InlineCompletionMiddleware { + provideInlineCompletionItems?: (this: void, document: TextDocument, position: VPosition, context: VInlineCompletionContext, token: CancellationToken, next: ProvideInlineCompletionItemsSignature) => ProviderResult; +} + +export type InlineCompletionProviderShape = { + provider: InlineCompletionItemProvider; +}; + +export class InlineCompletionItemFeature extends TextDocumentLanguageFeature { + + constructor(client: FeatureClient) { + super(client, InlineCompletionRequest.type); + } + + public fillClientCapabilities(capabilities: ClientCapabilities): void { + let inlineCompletion = ensure(ensure(capabilities, 'textDocument')!, 'inlineCompletion')!; + inlineCompletion.dynamicRegistration = true; + } + + public initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector): void { + const options = this.getRegistrationOptions(documentSelector, capabilities.inlineCompletionProvider); + if (!options) { + return; + } + + this.register({ + id: UUID.generateUuid(), + registerOptions: options + }); + } + + protected registerLanguageProvider(options: InlineCompletionRegistrationOptions): [Disposable, InlineCompletionItemProvider] { + const selector = options.documentSelector!; + const provider: InlineCompletionItemProvider = { + provideInlineCompletionItems: (document: TextDocument, position: VPosition, context: VInlineCompletionContext, token: CancellationToken): ProviderResult => { + const client = this._client; + const middleware = this._client.middleware; + const provideInlineCompletionItems: ProvideInlineCompletionItemsSignature = (document, position, context, token) => { + return client.sendRequest(InlineCompletionRequest.type, client.code2ProtocolConverter.asInlineCompletionParams(document, position, context), token).then((result) => { + if (token.isCancellationRequested) { + return null; + } + return client.protocol2CodeConverter.asInlineCompletionResult(result, token); + }, (error) => { + return client.handleFailedRequest(InlineCompletionRequest.type, token, error, null); + }); + }; + return middleware.provideInlineCompletionItems + ? middleware.provideInlineCompletionItems(document, position, context, token, provideInlineCompletionItems) + : provideInlineCompletionItems(document, position, context, token); + } + }; + return [Languages.registerInlineCompletionItemProvider(this._client.protocol2CodeConverter.asDocumentSelector(selector), provider), provider]; + } +} \ No newline at end of file diff --git a/client/src/common/protocolConverter.ts b/client/src/common/protocolConverter.ts index 0fce53ee2..b344d4a80 100644 --- a/client/src/common/protocolConverter.ts +++ b/client/src/common/protocolConverter.ts @@ -247,6 +247,13 @@ export interface Converter { asTypeHierarchyItems(items: ls.TypeHierarchyItem[] | null, token?: code.CancellationToken): Promise; asGlobPattern(pattern: ls.GlobPattern): code.GlobPattern | undefined; + + asInlineCompletionResult(value: undefined | null, token?: code.CancellationToken): Promise; + asInlineCompletionResult(value: ls.InlineCompletionList, token?: code.CancellationToken): Promise; + asInlineCompletionResult(value: ls.InlineCompletionItem[], token?: code.CancellationToken): Promise; + asInlineCompletionResult(value: ls.InlineCompletionItem[] | ls.InlineCompletionList | undefined | null, token?: code.CancellationToken): Promise; + + asInlineCompletionItem(item: ls.InlineCompletionItem): code.InlineCompletionItem; } export interface URIConverter { @@ -1420,6 +1427,41 @@ export function createConverter(uriConverter: URIConverter | undefined, trustMar return undefined; } + function asInlineCompletionResult(value: undefined | null, token?: code.CancellationToken): Promise; + function asInlineCompletionResult(value: ls.InlineCompletionList, token?: code.CancellationToken): Promise; + function asInlineCompletionResult(value: ls.InlineCompletionItem[], token?: code.CancellationToken): Promise; + function asInlineCompletionResult(value: ls.InlineCompletionItem[] | ls.InlineCompletionList | undefined | null, token?: code.CancellationToken): Promise; + async function asInlineCompletionResult(value: ls.InlineCompletionItem[] | ls.InlineCompletionList | undefined | null, token?: code.CancellationToken): Promise { + if (!value) { + return undefined; + } + if (Array.isArray(value)) { + return async.map(value, (item) => asInlineCompletionItem(item), token); + } + const list = value; + const converted = await async.map(list.items, (item) => { + return asInlineCompletionItem(item); + }, token); + return new code.InlineCompletionList(converted); + } + + function asInlineCompletionItem(item: ls.InlineCompletionItem): code.InlineCompletionItem { + let insertText: string | code.SnippetString; + if (typeof item.insertText === 'string') { + insertText = item.insertText; + } else { + insertText = new code.SnippetString(item.insertText.value); + } + + let command: code.Command | undefined = undefined; + if (item.command) {command = asCommand(item.command);} + + const inlineCompletionItem = new code.InlineCompletionItem(insertText, asRange(item.range), command); + + if (item.filterText) { inlineCompletionItem.filterText = item.filterText; } + + return inlineCompletionItem; + } return { asUri, @@ -1493,6 +1535,8 @@ export function createConverter(uriConverter: URIConverter | undefined, trustMar asLinkedEditingRanges: asLinkedEditingRanges, asTypeHierarchyItem, asTypeHierarchyItems, - asGlobPattern + asGlobPattern, + asInlineCompletionResult, + asInlineCompletionItem }; } diff --git a/client/src/node/main.ts b/client/src/node/main.ts index d4a6ed35f..dac300f3a 100644 --- a/client/src/node/main.ts +++ b/client/src/node/main.ts @@ -23,7 +23,7 @@ import semverSatisfies = require('semver/functions/satisfies'); export * from 'vscode-languageserver-protocol/node'; export * from '../common/api'; -const REQUIRED_VSCODE_VERSION = '^1.67.0'; // do not change format, updated by `updateVSCode` script +const REQUIRED_VSCODE_VERSION = '^1.68.0'; // do not change format, updated by `updateVSCode` script export enum TransportKind { stdio, diff --git a/protocol/metaModel.json b/protocol/metaModel.json index 5b1f59019..8b8303746 100644 --- a/protocol/metaModel.json +++ b/protocol/metaModel.json @@ -975,6 +975,48 @@ "documentation": "The diagnostic refresh request definition.\n\n@since 3.17.0", "since": "3.17.0" }, + { + "method": "textDocument/inlineCompletion", + "result": { + "kind": "or", + "items": [ + { + "kind": "reference", + "name": "InlineCompletionList" + }, + { + "kind": "array", + "element": { + "kind": "reference", + "name": "InlineCompletionItem" + } + }, + { + "kind": "base", + "name": "null" + } + ] + }, + "messageDirection": "clientToServer", + "params": { + "kind": "reference", + "name": "InlineCompletionParams" + }, + "partialResult": { + "kind": "array", + "element": { + "kind": "reference", + "name": "InlineCompletionItem" + } + }, + "registrationOptions": { + "kind": "reference", + "name": "InlineCompletionRegistrationOptions" + }, + "documentation": "A request to provide inline completions in a document. The request's parameter is of\ntype {@link InlineCompletionParams}, the response is of type\n{@link InlineCompletion InlineCompletion[]} or a Thenable that resolves to such.\n\n@since 3.18.0\n@proposed", + "since": "3.18.0", + "proposed": true + }, { "method": "client/registerCapability", "result": { @@ -4035,6 +4077,128 @@ "documentation": "The params sent in a close notebook document notification.\n\n@since 3.17.0", "since": "3.17.0" }, + { + "name": "InlineCompletionParams", + "properties": [ + { + "name": "context", + "type": { + "kind": "reference", + "name": "InlineCompletionContext" + }, + "documentation": "Additional information about the context in which inline completions were\nrequested." + } + ], + "extends": [ + { + "kind": "reference", + "name": "TextDocumentPositionParams" + } + ], + "mixins": [ + { + "kind": "reference", + "name": "WorkDoneProgressParams" + } + ], + "documentation": "A parameter literal used in inline completion requests.\n\n@since 3.18.0\n@proposed", + "since": "3.18.0", + "proposed": true + }, + { + "name": "InlineCompletionList", + "properties": [ + { + "name": "items", + "type": { + "kind": "array", + "element": { + "kind": "reference", + "name": "InlineCompletionItem" + } + }, + "documentation": "The inline completion items" + } + ], + "documentation": "Represents a collection of {@link InlineCompletionItem inline completion items} to be presented in the editor.\n\n@since 3.18.0\n@proposed", + "since": "3.18.0", + "proposed": true + }, + { + "name": "InlineCompletionItem", + "properties": [ + { + "name": "insertText", + "type": { + "kind": "or", + "items": [ + { + "kind": "base", + "name": "string" + }, + { + "kind": "reference", + "name": "StringValue" + } + ] + }, + "documentation": "The text to replace the range with. Must be set." + }, + { + "name": "filterText", + "type": { + "kind": "base", + "name": "string" + }, + "optional": true, + "documentation": "A text that is used to decide if this inline completion should be shown. When `falsy` the {@link InlineCompletionItem.insertText} is used." + }, + { + "name": "range", + "type": { + "kind": "reference", + "name": "Range" + }, + "optional": true, + "documentation": "The range to replace. Must begin and end on the same line." + }, + { + "name": "command", + "type": { + "kind": "reference", + "name": "Command" + }, + "optional": true, + "documentation": "An optional {@link Command} that is executed *after* inserting this completion." + } + ], + "documentation": "An inline completion item represents a text snippet that is proposed inline to complete text that is being typed.\n\n@since 3.18.0\n@proposed", + "since": "3.18.0", + "proposed": true + }, + { + "name": "InlineCompletionRegistrationOptions", + "properties": [], + "extends": [ + { + "kind": "reference", + "name": "InlineCompletionOptions" + }, + { + "kind": "reference", + "name": "TextDocumentRegistrationOptions" + } + ], + "mixins": [ + { + "kind": "reference", + "name": "StaticRegistrationOptions" + } + ], + "documentation": "Inline completion options used during static or dynamic registration.\n\n@since 3.18.0\n@proposed", + "since": "3.18.0", + "proposed": true + }, { "name": "RegistrationParams", "properties": [ @@ -7594,6 +7758,68 @@ "documentation": "A literal to identify a notebook document in the client.\n\n@since 3.17.0", "since": "3.17.0" }, + { + "name": "InlineCompletionContext", + "properties": [ + { + "name": "triggerKind", + "type": { + "kind": "reference", + "name": "InlineCompletionTriggerKind" + }, + "documentation": "Describes how the inline completion was triggered." + }, + { + "name": "selectedCompletionInfo", + "type": { + "kind": "reference", + "name": "SelectedCompletionInfo" + }, + "optional": true, + "documentation": "Provides information about the currently selected item in the autocomplete widget if it is visible." + } + ], + "documentation": "Provides information about the context in which an inline completion was requested.\n\n@since 3.18.0\n@proposed", + "since": "3.18.0", + "proposed": true + }, + { + "name": "StringValue", + "properties": [ + { + "name": "kind", + "type": { + "kind": "stringLiteral", + "value": "snippet" + }, + "documentation": "The kind of string value." + }, + { + "name": "value", + "type": { + "kind": "base", + "name": "string" + }, + "documentation": "The snippet string." + } + ], + "documentation": "A string value used as a snippet is a template which allows to insert text\nand to control the editor cursor when insertion happens.\n\nA snippet can define tab stops and placeholders with `$1`, `$2`\nand `${3:foo}`. `$0` defines the final tab stop, it defaults to\nthe end of the snippet. Variables are defined with `$name` and\n`${name:default value}`.\n\n@since 3.18.0\n@proposed", + "since": "3.18.0", + "proposed": true + }, + { + "name": "InlineCompletionOptions", + "mixins": [ + { + "kind": "reference", + "name": "WorkDoneProgressOptions" + } + ], + "properties": [], + "documentation": "Inline completion options used during static registration.\n\n@since 3.18.0\n@proposed", + "since": "3.18.0", + "proposed": true + }, { "name": "Registration", "properties": [ @@ -8398,6 +8624,26 @@ "documentation": "The server has support for pull model diagnostics.\n\n@since 3.17.0", "since": "3.17.0" }, + { + "name": "inlineCompletionProvider", + "type": { + "kind": "or", + "items": [ + { + "kind": "base", + "name": "boolean" + }, + { + "kind": "reference", + "name": "InlineCompletionOptions" + } + ] + }, + "optional": true, + "documentation": "Inline completion options used during static registration.\n\n@since 3.18.0\n@proposed", + "since": "3.18.0", + "proposed": true + }, { "name": "workspace", "type": { @@ -9671,6 +9917,30 @@ "documentation": "A change describing how to move a `NotebookCell`\narray from state S to S'.\n\n@since 3.17.0", "since": "3.17.0" }, + { + "name": "SelectedCompletionInfo", + "properties": [ + { + "name": "range", + "type": { + "kind": "reference", + "name": "Range" + }, + "documentation": "The range that will be replaced if this completion item is accepted." + }, + { + "name": "text", + "type": { + "kind": "base", + "name": "string" + }, + "documentation": "The text the range will be replaced with if this completion is accepted." + } + ], + "documentation": "Describes the currently selected completion item.\n\n@since 3.18.0\n@proposed", + "since": "3.18.0", + "proposed": true + }, { "name": "ClientCapabilities", "properties": [ @@ -10606,6 +10876,17 @@ "optional": true, "documentation": "Capabilities specific to the diagnostic pull model.\n\n@since 3.17.0", "since": "3.17.0" + }, + { + "name": "inlineCompletion", + "type": { + "kind": "reference", + "name": "InlineCompletionClientCapabilities" + }, + "optional": true, + "documentation": "Client capabilities specific to inline completions.\n\n@since 3.18.0\n@proposed", + "since": "3.18.0", + "proposed": true } ], "documentation": "Text document specific client capabilities." @@ -12430,6 +12711,23 @@ "documentation": "Client capabilities specific to diagnostic pull requests.\n\n@since 3.17.0", "since": "3.17.0" }, + { + "name": "InlineCompletionClientCapabilities", + "properties": [ + { + "name": "dynamicRegistration", + "type": { + "kind": "base", + "name": "boolean" + }, + "optional": true, + "documentation": "Whether implementation supports dynamic registration for inline completion providers." + } + ], + "documentation": "Client capabilities specific to inline completions.\n\n@since 3.18.0\n@proposed", + "since": "3.18.0", + "proposed": true + }, { "name": "NotebookDocumentSyncClientCapabilities", "properties": [ @@ -13424,6 +13722,28 @@ ], "documentation": "Describes the content type that a client supports in various\nresult literals like `Hover`, `ParameterInfo` or `CompletionItem`.\n\nPlease note that `MarkupKinds` must not start with a `$`. This kinds\nare reserved for internal usage." }, + { + "name": "InlineCompletionTriggerKind", + "type": { + "kind": "base", + "name": "uinteger" + }, + "values": [ + { + "name": "Invoked", + "value": 0, + "documentation": "Completion was triggered explicitly by a user gesture." + }, + { + "name": "Automatic", + "value": 1, + "documentation": "Completion was triggered automatically while editing." + } + ], + "documentation": "Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered.\n\n@since 3.18.0\n@proposed", + "since": "3.18.0", + "proposed": true + }, { "name": "PositionEncodingKind", "type": { diff --git a/protocol/src/common/protocol.inlineCompletion.ts b/protocol/src/common/protocol.inlineCompletion.ts new file mode 100644 index 000000000..d69d959c6 --- /dev/null +++ b/protocol/src/common/protocol.inlineCompletion.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { InlineCompletionItem, InlineCompletionContext, InlineCompletionList } from 'vscode-languageserver-types'; +import { RequestHandler } from 'vscode-jsonrpc'; + +import { MessageDirection, ProtocolRequestType } from './messages'; +import type { TextDocumentRegistrationOptions, WorkDoneProgressOptions, StaticRegistrationOptions, WorkDoneProgressParams, TextDocumentPositionParams } from './protocol'; + +// ---- capabilities + +/** + * Client capabilities specific to inline completions. + * + * @since 3.18.0 + * @proposed + */ +export type InlineCompletionClientCapabilities = { + /** + * Whether implementation supports dynamic registration for inline completion providers. + */ + dynamicRegistration?: boolean; +}; + +/** + * Inline completion options used during static registration. + * + * @since 3.18.0 + * @proposed + */ +export type InlineCompletionOptions = WorkDoneProgressOptions; + +/** + * Inline completion options used during static or dynamic registration. + * + * @since 3.18.0 + * @proposed + */ +export type InlineCompletionRegistrationOptions = InlineCompletionOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions; + +/** + * A parameter literal used in inline completion requests. + * + * @since 3.18.0 + * @proposed + */ +export type InlineCompletionParams = WorkDoneProgressParams & TextDocumentPositionParams & { + /** + * Additional information about the context in which inline completions were + * requested. + */ + context: InlineCompletionContext; +}; + +/** + * A request to provide inline completions in a document. The request's parameter is of + * type {@link InlineCompletionParams}, the response is of type + * {@link InlineCompletion InlineCompletion[]} or a Thenable that resolves to such. + * + * @since 3.18.0 + * @proposed + */ +export namespace InlineCompletionRequest { + export const method: 'textDocument/inlineCompletion' = 'textDocument/inlineCompletion'; + export const messageDirection: MessageDirection = MessageDirection.clientToServer; + export const type = new ProtocolRequestType(method); + export type HandlerSignature = RequestHandler; +} diff --git a/protocol/src/common/protocol.ts b/protocol/src/common/protocol.ts index 704e0d8f5..ced2a8bd2 100644 --- a/protocol/src/common/protocol.ts +++ b/protocol/src/common/protocol.ts @@ -121,6 +121,8 @@ import { DidCloseNotebookDocumentNotification } from './protocol.notebook'; +import { InlineCompletionClientCapabilities, InlineCompletionOptions, InlineCompletionParams, InlineCompletionRegistrationOptions, InlineCompletionRequest } from './protocol.inlineCompletion'; + // @ts-ignore: to avoid inlining LocationLink as dynamic import let __noDynamicImport: LocationLink | undefined; @@ -740,6 +742,14 @@ export interface TextDocumentClientCapabilities { * @since 3.17.0 */ diagnostic?: DiagnosticClientCapabilities; + + /** + * Client capabilities specific to inline completions. + * + * @since 3.18.0 + * @proposed + */ + inlineCompletion?: InlineCompletionClientCapabilities; } export interface WindowClientCapabilities { @@ -1247,6 +1257,14 @@ export interface ServerCapabilities { */ diagnosticProvider?: DiagnosticOptions | DiagnosticRegistrationOptions; + /** + * Inline completion options used during static registration. + * + * @since 3.18.0 + * @proposed + */ + inlineCompletionProvider?: boolean | InlineCompletionOptions; + /** * Workspace specific server capabilities. */ @@ -3847,7 +3865,9 @@ export { VersionedNotebookDocumentIdentifier, NotebookDocumentSyncOptions, NotebookDocumentSyncRegistrationOptions, NotebookDocumentSyncRegistrationType, DidOpenNotebookDocumentParams, DidOpenNotebookDocumentNotification, NotebookCellArrayChange, NotebookDocumentChangeEvent, DidChangeNotebookDocumentParams, DidChangeNotebookDocumentNotification, DidSaveNotebookDocumentParams, DidSaveNotebookDocumentNotification, DidCloseNotebookDocumentParams, - DidCloseNotebookDocumentNotification + DidCloseNotebookDocumentNotification, + // Inline Completions + InlineCompletionClientCapabilities, InlineCompletionOptions, InlineCompletionParams, InlineCompletionRegistrationOptions, InlineCompletionRequest }; // To be backwards compatible diff --git a/server/src/common/api.ts b/server/src/common/api.ts index a465f51cc..690b0c29b 100644 --- a/server/src/common/api.ts +++ b/server/src/common/api.ts @@ -7,6 +7,8 @@ import { _, Features, _Connection, _LanguagesImpl } from './server'; import { SemanticTokensBuilder } from './semanticTokens'; import type { WorkDoneProgressReporter, WorkDoneProgressServerReporter, ResultProgressReporter } from './progress'; +import * as ic from './inlineCompletion.proposed'; + export * from 'vscode-languageserver-protocol/'; export { WorkDoneProgressReporter, WorkDoneProgressServerReporter, ResultProgressReporter }; export { SemanticTokensBuilder }; @@ -17,9 +19,10 @@ export { NotebookDocuments }; export * from './server'; export namespace ProposedFeatures { - export const all: Features<_, _, _, _, _, _, _, _> = { + export const all: Features<_, _, _, _, _, _, ic.InlineCompletionFeatureShape, _> = { __brand: 'features', + languages: ic.InlineCompletionFeature }; - export type Connection = _Connection<_, _, _, _, _, _, _, _>; + export type Connection = _Connection<_, _, _, _, _, _, ic.InlineCompletionFeatureShape, _>; } \ No newline at end of file diff --git a/server/src/common/inlineCompletion.proposed.ts b/server/src/common/inlineCompletion.proposed.ts new file mode 100644 index 000000000..d1b9f7d8f --- /dev/null +++ b/server/src/common/inlineCompletion.proposed.ts @@ -0,0 +1,39 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import { InlineCompletionItem, Disposable, InlineCompletionParams, InlineCompletionList, InlineCompletionRequest } from 'vscode-languageserver-protocol'; + +import type { Feature, _Languages, ServerRequestHandler } from './server'; + +/** + * Shape of the inline completions feature + * + * @since 3.18.0 + */ +export interface InlineCompletionFeatureShape { + inlineCompletion: { + /** + * Installs a handler for the inline completions request. + * + * @param handler The corresponding handler. + */ + on(handler: ServerRequestHandler): Disposable; + }; +} + +export const InlineCompletionFeature: Feature<_Languages, InlineCompletionFeatureShape> = (Base) => { + return class extends Base implements InlineCompletionFeatureShape { + public get inlineCompletion() { + return { + on: (handler: ServerRequestHandler): Disposable => { + return this.connection.onRequest(InlineCompletionRequest.type, (params, cancel) => { + return handler(params, cancel, this.attachWorkDoneProgress(params)); + }); + } + }; + } + }; +}; \ No newline at end of file diff --git a/server/src/common/server.ts b/server/src/common/server.ts index 1a7058f23..9ae4cc745 100644 --- a/server/src/common/server.ts +++ b/server/src/common/server.ts @@ -39,6 +39,7 @@ import { FileOperationsFeature, FileOperationsFeatureShape } from './fileOperati import { LinkedEditingRangeFeature, LinkedEditingRangeFeatureShape } from './linkedEditingRange'; import { TypeHierarchyFeatureShape, TypeHierarchyFeature } from './typeHierarchy'; import { InlineValueFeatureShape, InlineValueFeature } from './inlineValue'; +// import { InlineCompletionFeatureShape, InlineCompletionFeature } from './inlineCompletion.proposed'; import { InlayHintFeatureShape, InlayHintFeature } from './inlayHint'; import { DiagnosticFeatureShape, DiagnosticFeature } from './diagnostic'; import { NotebookSyncFeatureShape, NotebookSyncFeature } from './notebook'; diff --git a/types/src/main.ts b/types/src/main.ts index 4723bd2f1..eb3ebe455 100644 --- a/types/src/main.ts +++ b/types/src/main.ts @@ -4087,6 +4087,156 @@ export namespace InlayHint { } } +/** + * A string value used as a snippet is a template which allows to insert text + * and to control the editor cursor when insertion happens. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Variables are defined with `$name` and + * `${name:default value}`. + * + * @since 3.18.0 + * @proposed + */ +export interface StringValue { + /** + * The kind of string value. + */ + kind: 'snippet'; + /** + * The snippet string. + */ + value: string; +} + +export namespace StringValue { + export function create(value: string): StringValue { + return { value, kind: 'snippet' }; + } +} + +/** + * An inline completion item represents a text snippet that is proposed inline to complete text that is being typed. + * + * @since 3.18.0 + * @proposed + */ +export interface InlineCompletionItem { + /** + * The text to replace the range with. Must be set. + */ + insertText: string | StringValue; + + /** + * A text that is used to decide if this inline completion should be shown. When `falsy` the {@link InlineCompletionItem.insertText} is used. + */ + filterText?: string; + + /** + * The range to replace. Must begin and end on the same line. + */ + range?: Range; + + /** + * An optional {@link Command} that is executed *after* inserting this completion. + */ + command?: Command; +} + +export namespace InlineCompletionItem { + export function create(insertText: string | StringValue, filterText?: string, range?: Range, command?: Command): InlineCompletionItem { + return { insertText, filterText, range, command }; + } +} + +/** + * Represents a collection of {@link InlineCompletionItem inline completion items} to be presented in the editor. + * + * @since 3.18.0 + * @proposed + */ +export interface InlineCompletionList { + /** + * The inline completion items + */ + items: InlineCompletionItem[]; +} + +export namespace InlineCompletionList { + export function create(items: InlineCompletionItem[]): InlineCompletionList { + return { items }; + } +} + +/** + * Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. + * + * @since 3.18.0 + * @proposed + */ +export namespace InlineCompletionTriggerKind { + /** + * Completion was triggered explicitly by a user gesture. + */ + export const Invoked: 0 = 0; + + /** + * Completion was triggered automatically while editing. + */ + export const Automatic: 1 = 1; +} + +export type InlineCompletionTriggerKind = 0 | 1; + +/** + * Describes the currently selected completion item. + * + * @since 3.18.0 + * @proposed + */ +export interface SelectedCompletionInfo { + /** + * The range that will be replaced if this completion item is accepted. + */ + range: Range; + + /** + * The text the range will be replaced with if this completion is accepted. + */ + text: string; +} + +export namespace SelectedCompletionInfo { + export function create(range: Range, text: string): SelectedCompletionInfo { + return { range, text }; + } +} + +/** + * Provides information about the context in which an inline completion was requested. + * + * @since 3.18.0 + * @proposed + */ +export interface InlineCompletionContext { + /** + * Describes how the inline completion was triggered. + */ + triggerKind: InlineCompletionTriggerKind; + + /** + * Provides information about the currently selected item in the autocomplete widget if it is visible. + */ + selectedCompletionInfo?: SelectedCompletionInfo; +} + +export namespace InlineCompletionContext{ + export function create(triggerKind: InlineCompletionTriggerKind, selectedCompletionInfo?: SelectedCompletionInfo): InlineCompletionContext { + return { triggerKind, selectedCompletionInfo }; + } +} + /** * A workspace folder inside a client. */