Skip to content

Commit

Permalink
Add support for textdocument/inlineCompletions
Browse files Browse the repository at this point in the history
  • Loading branch information
c-claeys committed Feb 15, 2023
1 parent 9b74202 commit 90a9952
Show file tree
Hide file tree
Showing 16 changed files with 736 additions and 48 deletions.
20 changes: 20 additions & 0 deletions client-node-tests/src/converter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.InsertTextFormat.PlainText),
proto.InlineCompletionItem.create('insert text', 'in', proto.Range.create(1, 2, 6, 7), undefined, proto.InsertTextFormat.Snippet),
];

const result = await p2c.asInlineCompletionResult(items);
ok(result.every((r) => r.range 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',
Expand Down
22 changes: 22 additions & 0 deletions client-node-tests/src/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ suite('Client integration', () => {
delta: true
}
},
inlineCompletionProvider: {},
workspace: {
fileOperations: {
didCreate: { filters: [{ scheme: fsProvider.scheme, pattern: { glob: '**/created-static/**{/,/*.txt}' } }] },
Expand Down Expand Up @@ -1439,6 +1440,27 @@ suite('Client integration', () => {
assert.strictEqual((resolvedHint?.label as vscode.InlayHintLabelPart[])[0].tooltip, 'tooltip');
});

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);
Expand Down
9 changes: 8 additions & 1 deletion client-node-tests/src/servers/testServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ 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';
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -512,6 +513,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<null, null, never, any, any>('testing/sendSampleProgress'),
async (_, __) => {
Expand Down
20 changes: 10 additions & 10 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "vscode-languageclient",
"description": "VSCode Language client implementation",
"version": "8.1.0-next.6",
"version": "8.1.0-next.7",
"author": "Microsoft Corporation",
"license": "MIT",
"engines": {
"vscode": "^1.67.0"
"vscode": "^1.68.0"
},
"repository": {
"type": "git",
Expand All @@ -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": {
Expand Down
10 changes: 7 additions & 3 deletions client/src/common/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -35,7 +35,7 @@ import {
ImplementationRequest, SelectionRangeRequest, TypeDefinitionRequest, CallHierarchyPrepareRequest, SemanticTokensRegistrationType, LinkedEditingRangeRequest,
TypeHierarchyPrepareRequest, InlineValueRequest, InlayHintRequest, WorkspaceSymbolRequest, TextDocumentRegistrationOptions, FileOperationRegistrationOptions,
ConnectionOptions, PositionEncodingKind, DocumentDiagnosticRequest, NotebookDocumentSyncRegistrationType, NotebookDocumentSyncRegistrationOptions, ErrorCodes,
MessageStrategy, DidOpenTextDocumentParams, CodeLensResolveRequest, CompletionResolveRequest, CodeActionResolveRequest, InlayHintResolveRequest, DocumentLinkResolveRequest, WorkspaceSymbolResolveRequest
MessageStrategy, DidOpenTextDocumentParams, CodeLensResolveRequest, CompletionResolveRequest, CodeActionResolveRequest, InlayHintResolveRequest, DocumentLinkResolveRequest, WorkspaceSymbolResolveRequest, InlineCompletionRequest, InlineCompletionRegistrationOptions
} from 'vscode-languageserver-protocol';

import * as c2p from './codeConverter';
Expand Down Expand Up @@ -89,6 +89,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';
Expand All @@ -105,6 +106,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.
Expand Down Expand Up @@ -325,7 +327,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;
LinkedEditingRangeMiddleware & TypeHierarchyMiddleware & InlineValueMiddleware & InlayHintsMiddleware & NotebookDocumentMiddleware & DiagnosticProviderMiddleware & InlineCompletionMiddleware;

export type LanguageClientOptions = {
documentSelector?: DocumentSelector | string[];
Expand Down Expand Up @@ -1670,6 +1672,7 @@ export abstract class BaseLanguageClient implements FeatureClient<Middleware, La
getFeature(request: typeof WorkspaceSymbolRequest.method): DynamicFeature<TextDocumentRegistrationOptions> & WorkspaceProviderFeature<WorkspaceSymbolProvider>;
getFeature(request: typeof DocumentDiagnosticRequest.method): DynamicFeature<TextDocumentRegistrationOptions> & TextDocumentProviderFeature<DiagnosticProviderShape> | undefined;
getFeature(request: typeof NotebookDocumentSyncRegistrationType.method): DynamicFeature<NotebookDocumentSyncRegistrationOptions> & NotebookDocumentProviderShape | undefined;
getFeature(request: typeof InlineCompletionRequest.method): DynamicFeature<InlineCompletionRegistrationOptions> & TextDocumentProviderFeature<InlineCompletionItemProvider>;
public getFeature(request: string): DynamicFeature<any> | undefined {
return this._dynamicFeatures.get(request);
}
Expand Down Expand Up @@ -1739,6 +1742,7 @@ export abstract class BaseLanguageClient implements FeatureClient<Middleware, La
this.registerFeature(new InlayHintsFeature(this));
this.registerFeature(new DiagnosticFeature(this));
this.registerFeature(new NotebookDocumentSyncFeature(this));
this.registerFeature(new InlineCompletionItemFeature(this));
}

public registerProposedFeatures() {
Expand Down
10 changes: 9 additions & 1 deletion client/src/common/codeConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,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 {
Expand Down Expand Up @@ -757,6 +759,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; }
Expand Down Expand Up @@ -938,6 +945,7 @@ export function createConverter(uriConverter?: URIConverter): Converter {
asCallHierarchyItem,
asTypeHierarchyItem,
asInlayHint,
asWorkspaceSymbol
asWorkspaceSymbol,
asInlineCompletionParams
};
}
80 changes: 80 additions & 0 deletions client/src/common/inlineCompletion.ts
Original file line number Diff line number Diff line change
@@ -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<VInlineCompletionItem[] | VInlineCompletionList>;
}

export interface InlineCompletionMiddleware {
provideInlineCompletionItems?: (this: void, document: TextDocument, position: VPosition, context: VInlineCompletionContext, token: CancellationToken, next: ProvideInlineCompletionItemsSignature) => ProviderResult<VInlineCompletionItem[] | VInlineCompletionList>;
}

export type InlineCompletionProviderShape = {
provider: InlineCompletionItemProvider;
};

export class InlineCompletionItemFeature extends TextDocumentLanguageFeature<boolean | InlineCompletionOptions, InlineCompletionRegistrationOptions, InlineCompletionItemProvider, InlineCompletionMiddleware> {

constructor(client: FeatureClient<InlineCompletionMiddleware>) {
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<VInlineCompletionList | VInlineCompletionItem[]> => {
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];
}
}
Loading

0 comments on commit 90a9952

Please sign in to comment.