Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inline completions #1190

Merged
merged 12 commits into from
May 22, 2023
16 changes: 8 additions & 8 deletions client-node-tests/package-lock.json

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

7 changes: 2 additions & 5 deletions client-node-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,14 @@
"description": "",
"version": "0.0.1",
"engines": {
"vscode": "^1.67.0"
"vscode": "^1.68.0"
},
"categories": [
"Other"
],
"activationEvents": [
"*"
],
"enabledApiProposals": [
"notebookContentProvider"
],
"main": "./extension.js",
"contributes": {},
"scripts": {
Expand All @@ -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",
Expand Down
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.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',
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 @@ -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);
Expand Down
11 changes: 9 additions & 2 deletions client-node-tests/src/servers/testServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
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 @@ -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<null, null, never, any, any>('testing/sendSampleProgress'),
async (_, __) => {
Expand Down
16 changes: 8 additions & 8 deletions client/package-lock.json

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

4 changes: 2 additions & 2 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,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 @@ -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';
Expand Down Expand Up @@ -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';
Expand All @@ -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.
Expand Down Expand Up @@ -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[];
Expand Down Expand Up @@ -1734,6 +1736,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 @@ -2213,6 +2216,7 @@ function createConnection(input: MessageReader, output: MessageWriter, errorHand
export namespace ProposedFeatures {
export function createAll(_client: FeatureClient<Middleware, LanguageClientOptions>): (StaticFeature | DynamicFeature<any>)[] {
let result: (StaticFeature | DynamicFeature<any>)[] = [
new InlineCompletionItemFeature(_client)
];
return result;
}
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 @@ -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 {
Expand Down Expand Up @@ -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; }
Expand Down Expand Up @@ -982,6 +989,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