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

Add support for textDocument/inlineValues #806

Merged
merged 3 commits into from
Oct 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ After cloning the repository, run `npm install` to install dependencies and `npm

Library specific changes are:

- all `sendNotification` methods now return a promise. Returning a promise was necessary since the actual writing of the message to the underlying transport is async and a client for example could not determine if a notification was handed of to the transport. This is a breaking change in the sense that it might result in floating promise and might be flagged by a linter.
- all `sendNotification` methods now return a promise. Returning a promise was necessary since the actual writing of the message to the underlying transport is async and a client for example could not determine if a notification was handed off to the transport. This is a breaking change in the sense that it might result in floating promise and might be flagged by a linter.
- `InlineValuesRequest` protocol added:
- New APIs in Types: `InlineValues`
- New APIs in Protocol: `InlineValuesRequest`, `InlineValuesParams`, `InlineValuesClientCapabilities`, `InlineValuesProviderOptions`,

## 3.16.0 Protocol, 6.0.0 JSON-RPC, 7.0.0 Client and 7.0.0 Server.

Expand Down
39 changes: 38 additions & 1 deletion client-node-tests/src/converter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,30 @@ suite('Protocol Converter', () => {
strictEqual('file://localhost/folder/file.vscode', result.toString());
});

test('InlineValues', () => {
const items: proto.InlineValue[] = [
proto.InlineValueText.create(proto.Range.create(1, 2, 8, 9), 'literalString'),
proto.InlineValueVariableLookup.create(proto.Range.create(1, 2, 8, 9), 'varName', false),
proto.InlineValueVariableLookup.create(proto.Range.create(1, 2, 8, 9), undefined, true),
proto.InlineValueEvaluatableExpression.create(proto.Range.create(1, 2, 8, 9), 'expression'),
proto.InlineValueEvaluatableExpression.create(proto.Range.create(1, 2, 8, 9), undefined),
];

let result = p2c.asInlineValues(<any>items);


ok(result.every((r) => r.range instanceof vscode.Range));
for (const r of result) {
rangeEqual(r.range, proto.Range.create(1, 2, 8, 9));
}

ok(result[0] instanceof vscode.InlineValueText && result[0].text === 'literalString');
ok(result[1] instanceof vscode.InlineValueVariableLookup && result[1].variableName === 'varName' && result[1].caseSensitiveLookup === false);
ok(result[2] instanceof vscode.InlineValueVariableLookup && result[2].variableName === undefined && result[2].caseSensitiveLookup === true);
ok(result[3] instanceof vscode.InlineValueEvaluatableExpression && result[3].expression === 'expression');
ok(result[4] instanceof vscode.InlineValueEvaluatableExpression && result[4].expression === undefined);
});

test('Bug #361', () => {
const item: proto.CompletionItem = {
'label': 'MyLabel',
Expand Down Expand Up @@ -1280,4 +1304,17 @@ suite('Code Converter', () => {
let result = converter.asUri(vscode.Uri.parse('file://localhost/folder/file'));
strictEqual('file://localhost/folder/file.vscode', result);
});
});

test('InlineValuesContext', () => {
const item: proto.InlineValuesContext = {
stoppedLocation: new vscode.Range(1, 2, 8, 9),
};

let result = c2p.asInlineValuesContext(<any>item);

strictEqual(result.stoppedLocation.start.line, 1);
strictEqual(result.stoppedLocation.start.character, 2);
strictEqual(result.stoppedLocation.end.line, 8);
strictEqual(result.stoppedLocation.end.character, 9);
});
});
31 changes: 31 additions & 0 deletions client-node-tests/src/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ suite('Client integration', () => {
foldingRangeProvider: true,
implementationProvider: true,
selectionRangeProvider: true,
inlineValuesProvider: {},
typeDefinitionProvider: true,
callHierarchyProvider: true,
semanticTokensProvider: {
Expand Down Expand Up @@ -647,6 +648,36 @@ suite('Client integration', () => {
assert.strictEqual(middlewareCalled, true);
});

test('Inline Values', async () => {
const provider = client.getFeature(lsclient.InlineValuesRequest.method).getProvider(document);
isDefined(provider);
const results = (await provider.provideInlineValues(document, range, { frameId: 1, stoppedLocation: range }, tokenSource.token));

isArray(results, undefined, 3);

for (const r of results) {
rangeEqual(r.range, 1, 2, 3, 4);
}

assert.ok(results[0] instanceof vscode.InlineValueText);
assert.strictEqual((results[0] as vscode.InlineValueText).text, 'text');

assert.ok(results[1] instanceof vscode.InlineValueVariableLookup);
assert.strictEqual((results[1] as vscode.InlineValueVariableLookup).variableName, 'variableName');

assert.ok(results[2] instanceof vscode.InlineValueEvaluatableExpression);
assert.strictEqual((results[2] as vscode.InlineValueEvaluatableExpression).expression, 'expression');

let middlewareCalled: boolean = false;
middleware.provideInlineValues = (d, r, c, t, n) => {
middlewareCalled = true;
return n(d, r, c, t);
};
await provider.provideInlineValues(document, range, { frameId: 1, stoppedLocation: range }, tokenSource.token);
middleware.provideInlineValues = undefined;
assert.strictEqual(middlewareCalled, true);
});

test('Type Definition', async () => {
const provider = client.getFeature(lsclient.TypeDefinitionRequest.method).getProvider(document);
isDefined(provider);
Expand Down
10 changes: 10 additions & 0 deletions client-node-tests/src/servers/testServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
DiagnosticTag, CompletionItemTag, TextDocumentSyncKind, MarkupKind, SignatureHelp, SignatureInformation, ParameterInformation,
Location, Range, DocumentHighlight, DocumentHighlightKind, CodeAction, Command, TextEdit, Position, DocumentLink,
ColorInformation, Color, ColorPresentation, FoldingRange, SelectionRange, SymbolKind, ProtocolRequestType, WorkDoneProgress,
InlineValueText, InlineValueVariableLookup, InlineValueEvaluatableExpression,
WorkDoneProgressCreateRequest, WillCreateFilesRequest, WillRenameFilesRequest, WillDeleteFilesRequest, DidDeleteFilesNotification,
DidRenameFilesNotification, DidCreateFilesNotification, Proposed, ProposedFeatures, Diagnostic, DiagnosticSeverity, TypeHierarchyItem
} from '../../../server/node';
Expand Down Expand Up @@ -81,6 +82,7 @@ connection.onInitialize((params: InitializeParams): any => {
foldingRangeProvider: true,
implementationProvider: true,
selectionRangeProvider: true,
inlineValuesProvider: {},
typeDefinitionProvider: true,
callHierarchyProvider: true,
semanticTokensProvider: {
Expand Down Expand Up @@ -297,6 +299,14 @@ connection.onSelectionRanges((_params) => {
];
});

connection.onInlineValues((_params) => {
return [
InlineValueText.create(Range.create(1, 2, 3, 4), 'text'),
InlineValueVariableLookup.create(Range.create(1, 2, 3, 4), 'variableName', false),
InlineValueEvaluatableExpression.create(Range.create(1, 2, 3, 4), 'expression'),
];
});

let lastFileOperationRequest: unknown;
connection.workspace.onDidCreateFiles((params) => { lastFileOperationRequest = { type: 'create', params }; });
connection.workspace.onDidRenameFiles((params) => { lastFileOperationRequest = { type: 'rename', params }; });
Expand Down
8 changes: 5 additions & 3 deletions client/src/common/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
DocumentRangeFormattingEditProvider, OnTypeFormattingEditProvider, RenameProvider, DocumentLinkProvider, DocumentColorProvider, DeclarationProvider,
FoldingRangeProvider, ImplementationProvider, SelectionRangeProvider, TypeDefinitionProvider, WorkspaceSymbolProvider, CallHierarchyProvider,
DocumentSymbolProviderMetadata, EventEmitter, env as Env, TextDocumentShowOptions, FileWillCreateEvent, FileWillRenameEvent, FileWillDeleteEvent, FileCreateEvent, FileDeleteEvent, FileRenameEvent,
LinkedEditingRangeProvider, Event as VEvent, CancellationError, TypeHierarchyProvider as VTypeHierarchyProvider
LinkedEditingRangeProvider, Event as VEvent, CancellationError, InlineValuesProvider, TypeHierarchyProvider as VTypeHierarchyProvider
} from 'vscode';

import {
Expand Down Expand Up @@ -52,7 +52,7 @@ import {
CancellationStrategy, SaveOptions, LSPErrorCodes, CodeActionResolveRequest, RegistrationType, SemanticTokensRegistrationType, InsertTextMode, ShowDocumentRequest,
FileOperationRegistrationOptions, WillCreateFilesRequest, WillRenameFilesRequest, WillDeleteFilesRequest, DidCreateFilesNotification, DidDeleteFilesNotification, DidRenameFilesNotification,
ShowDocumentParams, ShowDocumentResult, LinkedEditingRangeRequest, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd, WorkDoneProgressReport, PrepareSupportDefaultBehavior,
SemanticTokensRequest, SemanticTokensRangeRequest, SemanticTokensDeltaRequest, Proposed
SemanticTokensRequest, SemanticTokensRangeRequest, SemanticTokensDeltaRequest, Proposed, InlineValuesRequest
} from 'vscode-languageserver-protocol';

import { toJSONObject } from './configuration';
Expand All @@ -69,6 +69,7 @@ import type { SemanticTokensMiddleware, SemanticTokensProviders } from './semant
import type { FileOperationsMiddleware } from './fileOperations';
import type { LinkedEditingRangeMiddleware } from './linkedEditingRange';
import type { DiagnosticFeatureProvider } from './proposed.diagnostic';
import type { InlineValuesProviderMiddleware } from './inlineValues';
import type { TypeHierarchyMiddleware } from './proposed.typeHierarchy';

import * as c2p from './codeConverter';
Expand Down Expand Up @@ -517,7 +518,7 @@ export interface _Middleware {

export type Middleware = _Middleware & TypeDefinitionMiddleware & ImplementationMiddleware & ColorProviderMiddleware &
FoldingRangeProviderMiddleware & DeclarationMiddleware & SelectionRangeProviderMiddleware & CallHierarchyMiddleware & SemanticTokensMiddleware &
LinkedEditingRangeMiddleware & TypeHierarchyMiddleware;
LinkedEditingRangeMiddleware & TypeHierarchyMiddleware & InlineValuesProviderMiddleware;

export interface LanguageClientOptions {
documentSelector?: DocumentSelector | string[];
Expand Down Expand Up @@ -3618,6 +3619,7 @@ export abstract class BaseLanguageClient {
public getFeature(request: typeof FoldingRangeRequest.method): DynamicFeature<TextDocumentRegistrationOptions> & TextDocumentProviderFeature<FoldingRangeProvider>;
public getFeature(request: typeof ImplementationRequest.method): DynamicFeature<TextDocumentRegistrationOptions> & TextDocumentProviderFeature<ImplementationProvider>;
public getFeature(request: typeof SelectionRangeRequest.method): DynamicFeature<TextDocumentRegistrationOptions> & TextDocumentProviderFeature<SelectionRangeProvider>;
public getFeature(request: typeof InlineValuesRequest.method): DynamicFeature<TextDocumentRegistrationOptions> & TextDocumentProviderFeature<InlineValuesProvider>;
public getFeature(request: typeof TypeDefinitionRequest.method): DynamicFeature<TextDocumentRegistrationOptions> & TextDocumentProviderFeature<TypeDefinitionProvider>;
public getFeature(request: typeof CallHierarchyPrepareRequest.method): DynamicFeature<TextDocumentRegistrationOptions> & TextDocumentProviderFeature<CallHierarchyProvider>;
public getFeature(request: typeof SemanticTokensRegistrationType.method): DynamicFeature<TextDocumentRegistrationOptions> & TextDocumentProviderFeature<SemanticTokensProviders>;
Expand Down
9 changes: 9 additions & 0 deletions client/src/common/codeConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ export interface Converter {

asCodeActionContext(context: code.CodeActionContext): proto.CodeActionContext;

asInlineValuesContext(context: code.InlineValueContext): proto.InlineValuesContext;

asCommand(item: code.Command): proto.Command;

asCodeLens(item: code.CodeLens): proto.CodeLens;
Expand Down Expand Up @@ -727,6 +729,12 @@ export function createConverter(uriConverter?: URIConverter): Converter {
return item.value;
}

function asInlineValuesContext(context: code.InlineValueContext): proto.InlineValuesContext {
if (context === undefined || context === null) {
return context;
}
return proto.InlineValuesContext.create(context.stoppedLocation);
}

function asCommand(item: code.Command): proto.Command {
let result = proto.Command.create(item.title, item.command);
Expand Down Expand Up @@ -853,6 +861,7 @@ export function createConverter(uriConverter?: URIConverter): Converter {
asReferenceParams,
asCodeAction,
asCodeActionContext,
asInlineValuesContext,
asCommand,
asCodeLens,
asFormattingOptions,
Expand Down
2 changes: 2 additions & 0 deletions client/src/common/commonClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { WorkspaceFoldersFeature } from './workspaceFolders';
import { FoldingRangeFeature } from './foldingRange';
import { DeclarationFeature } from './declaration';
import { SelectionRangeFeature } from './selectionRange';
import { InlineValueFeature } from './inlineValues';
import { ProgressFeature } from './progress';
import { CallHierarchyFeature } from './callHierarchy';
import { SemanticTokensFeature } from './semanticTokens';
Expand Down Expand Up @@ -43,6 +44,7 @@ export abstract class CommonLanguageClient extends BaseLanguageClient {
this.registerFeature(new FoldingRangeFeature(this));
this.registerFeature(new DeclarationFeature(this));
this.registerFeature(new SelectionRangeFeature(this));
this.registerFeature(new InlineValueFeature(this));
this.registerFeature(new ProgressFeature(this));
this.registerFeature(new CallHierarchyFeature(this));
this.registerFeature(new SemanticTokensFeature(this));
Expand Down
74 changes: 74 additions & 0 deletions client/src/common/inlineValues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/* --------------------------------------------------------------------------------------------
* 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, Disposable, TextDocument, ProviderResult, Range as VRange, InlineValueContext as VInlineValueContext, InlineValue as VInlineValue, InlineValuesProvider } from 'vscode';

import {
ClientCapabilities, CancellationToken, ServerCapabilities, DocumentSelector,
InlineValuesParams, InlineValuesRequest, InlineValuesOptions, InlineValuesRegistrationOptions
} from 'vscode-languageserver-protocol';

import { TextDocumentFeature, BaseLanguageClient } from './client';

function ensure<T, K extends keyof T>(target: T, key: K): T[K] {
if (target[key] === void 0) {
target[key] = Object.create(null) as any;
}
return target[key];
}

export interface ProvideInlineValuesSignature {
(this: void, document: TextDocument, viewPort: VRange, context: VInlineValueContext, token: CancellationToken): ProviderResult<VInlineValue[]>;
}

export interface InlineValuesProviderMiddleware {
provideInlineValues?: (this: void, document: TextDocument, viewPort: VRange, context: VInlineValueContext, token: CancellationToken, next: ProvideInlineValuesSignature) => ProviderResult<VInlineValue[]>;
}

export class InlineValueFeature extends TextDocumentFeature<boolean | InlineValuesOptions, InlineValuesRegistrationOptions, InlineValuesProvider> {
constructor(client: BaseLanguageClient) {
super(client, InlineValuesRequest.type);
}

public fillClientCapabilities(capabilities: ClientCapabilities): void {
let capability = ensure(ensure(capabilities, 'textDocument')!, 'inlineValues')!;
capability.dynamicRegistration = true;
}

public initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector): void {
let [id, options] = this.getRegistration(documentSelector, capabilities.inlineValuesProvider);
if (!id || !options) {
return;
}
this.register({ id: id, registerOptions: options });
}

protected registerLanguageProvider(options: InlineValuesRegistrationOptions): [Disposable, InlineValuesProvider] {
const provider: InlineValuesProvider = {
provideInlineValues: (document, viewPort, context, token) => {
const client = this._client;
const provideInlineValues: ProvideInlineValuesSignature = (document, viewPort, context, token) => {
const requestParams: InlineValuesParams = {
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
viewPort: client.code2ProtocolConverter.asRange(viewPort),
context: client.code2ProtocolConverter.asInlineValuesContext(context)
};
return client.sendRequest(InlineValuesRequest.type, requestParams, token).then(
(values) => client.protocol2CodeConverter.asInlineValues(values),
(error: any) => {
return client.handleFailedRequest(InlineValuesRequest.type, token, error, null);
}
);
};
const middleware = client.clientOptions.middleware!;
return middleware.provideInlineValues
? middleware.provideInlineValues(document, viewPort, context, token, provideInlineValues)
: provideInlineValues(document, viewPort, context, token);

}
};
return [Languages.registerInlineValuesProvider(options.documentSelector!, provider), provider];
}
}
45 changes: 43 additions & 2 deletions client/src/common/protocolConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ export interface Converter {
asSelectionRanges(selectionRanges: ls.SelectionRange[] | undefined | null): code.SelectionRange[] | undefined;
asSelectionRanges(selectionRanges: ls.SelectionRange[] | undefined | null): code.SelectionRange[] | undefined;

asInlineValue(value: ls.InlineValue): code.InlineValue;
asInlineValues(values: ls.InlineValue[]): code.InlineValue[];
asInlineValues(values: undefined | null): undefined;
asInlineValues(values: ls.InlineValue[] | undefined | null): code.InlineValue[] | undefined;
asInlineValues(values: ls.InlineValue[] | undefined | null): code.InlineValue[] | undefined;

asSemanticTokensLegend(value: ls.SemanticTokensLegend): code.SemanticTokensLegend;

asSemanticTokens(value: ls.SemanticTokens): code.SemanticTokens;
Expand Down Expand Up @@ -622,7 +628,7 @@ export function createConverter(uriConverter: URIConverter | undefined, trustMar
if (item.documentation !== undefined) { result.documentation = asDocumentation(item.documentation); }
if (item.parameters !== undefined) { result.parameters = asParameterInformations(item.parameters); }
if (item.activeParameter !== undefined) { result.activeParameter = item.activeParameter; }
{return result;}
{ return result; }
}

function asParameterInformations(item: ls.ParameterInformation[]): code.ParameterInformation[] {
Expand Down Expand Up @@ -762,7 +768,7 @@ export function createConverter(uriConverter: URIConverter | undefined, trustMar
}

function asSymbolTag(value: ls.SymbolTag): code.SymbolTag | undefined {
switch(value) {
switch (value) {
case ls.SymbolTag.Deprecated:
return code.SymbolTag.Deprecated;
default:
Expand Down Expand Up @@ -1098,6 +1104,39 @@ export function createConverter(uriConverter: URIConverter | undefined, trustMar
return result;
}

function asInlineValue(inlineValue: ls.InlineValue): code.InlineValue {
if (ls.InlineValueText.is(inlineValue)) {
return new code.InlineValueText(
asRange(inlineValue.range),
inlineValue.text,
);
} else if (ls.InlineValueVariableLookup.is(inlineValue)) {
return new code.InlineValueVariableLookup(
asRange(inlineValue.range),
inlineValue.variableName,
inlineValue.caseSensitiveLookup,
);
} else {
return new code.InlineValueEvaluatableExpression(
asRange(inlineValue.range),
inlineValue.expression,
);
}
}
function asInlineValues(inlineValues: ls.InlineValue[]): code.SelectionRange[];
function asInlineValues(inlineValues: undefined | null): undefined;
function asInlineValues(inlineValues: ls.SelectionRange[] | undefined | null): code.SelectionRange[] | undefined;
function asInlineValues(inlineValues: ls.SelectionRange[] | undefined | null): code.SelectionRange[] | undefined {
if (!Array.isArray(inlineValues)) {
return [];
}
let result: code.InlineValue[] = [];
for (let inlineValue of inlineValues) {
result.push(asInlineValue(inlineValue));
}
return result;
}

//----- call hierarchy

function asCallHierarchyItem(item: null): undefined;
Expand Down Expand Up @@ -1297,6 +1336,8 @@ export function createConverter(uriConverter: URIConverter | undefined, trustMar
asColorPresentations,
asSelectionRange,
asSelectionRanges,
asInlineValue,
asInlineValues,
asSemanticTokensLegend,
asSemanticTokens,
asSemanticTokensEdit,
Expand Down
Loading