Skip to content

Commit 2d8e6da

Browse files
committed
Add proposed textDocument/inlayHints to protocol & client.
This addresses the FR microsoft/language-server-protocol#956 And corresponds to the spec in microsoft/language-server-protocol#1249
1 parent 2645fb5 commit 2d8e6da

File tree

5 files changed

+308
-1
lines changed

5 files changed

+308
-1
lines changed

client/src/common/commonClient.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,13 @@ export abstract class CommonLanguageClient extends BaseLanguageClient {
5555
// Exporting proposed protocol.
5656

5757
import { DiagnosticFeature } from './proposed.diagnostic';
58+
import { InlayHintsFeature } from './proposed.inlayHints';
59+
5860
export namespace ProposedFeatures {
5961
export function createAll(_client: BaseLanguageClient): (StaticFeature | DynamicFeature<any>)[] {
6062
let result: (StaticFeature | DynamicFeature<any>)[] = [
61-
new DiagnosticFeature(_client)
63+
new DiagnosticFeature(_client),
64+
new InlayHintsFeature(_client),
6265
];
6366
return result;
6467
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/* --------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
* ------------------------------------------------------------------------------------------ */
5+
6+
import { languages as Languages, Disposable, TextDocument, Range, ProviderResult, InlayHintKind, InlayHint as VInlayHint, InlayHintsProvider } from 'vscode';
7+
8+
import {
9+
ClientCapabilities, CancellationToken, ServerCapabilities, DocumentSelector, Proposed
10+
} from 'vscode-languageserver-protocol';
11+
12+
import { TextDocumentFeature, BaseLanguageClient, Middleware } from './client';
13+
import * as p2c from './protocolConverter';
14+
15+
function ensure<T, K extends keyof T>(target: T, key: K): T[K] {
16+
if (target[key] === void 0) {
17+
target[key] = {} as any;
18+
}
19+
return target[key];
20+
}
21+
22+
export interface ProvideInlayHintsSignature {
23+
(this: void, document: TextDocument, range: Range, token: CancellationToken): ProviderResult<VInlayHint[]>;
24+
}
25+
26+
export interface InlayHintsMiddleware {
27+
provideInlayHints?: (this: void, document: TextDocument, range: Range, token: CancellationToken, next: ProvideInlayHintsSignature) => ProviderResult<VInlayHint[]>;
28+
}
29+
30+
namespace protocol2code {
31+
function asInlayHintKind(_: p2c.Converter, value: string | null | undefined) : InlayHintKind | undefined {
32+
switch (value) {
33+
case Proposed.InlayHintCategory.Parameter:
34+
return InlayHintKind.Parameter;
35+
case Proposed.InlayHintCategory.Type:
36+
return InlayHintKind.Type;
37+
default:
38+
return InlayHintKind.Other;
39+
}
40+
}
41+
export function asInlayHint(converter: p2c.Converter, item: Proposed.InlayHint) : VInlayHint {
42+
const result = new VInlayHint(item.label.trim(), converter.asPosition(item.position), asInlayHintKind(converter, item.category));
43+
result.whitespaceBefore = item.label.startsWith(' ');
44+
result.whitespaceAfter = item.label.endsWith(' ');
45+
return result;
46+
}
47+
}
48+
49+
export class InlayHintsFeature extends TextDocumentFeature<boolean | Proposed.InlayHintsOptions, Proposed.InlayHintsRegistrationOptions, InlayHintsProvider> {
50+
51+
constructor(client: BaseLanguageClient) {
52+
super(client, Proposed.InlayHintsRequest.type);
53+
}
54+
55+
public fillClientCapabilities(capabilities: ClientCapabilities & Proposed.$InlayHintsClientCapabilities): void {
56+
let capability = ensure(ensure(capabilities, 'textDocument')!, 'inlayHints')!;
57+
capability.dynamicRegistration = true;
58+
}
59+
60+
public initialize(capabilities: ServerCapabilities & Proposed.$InlayHintsServerCapabilities, documentSelector: DocumentSelector): void {
61+
let [id, options] = this.getRegistration(documentSelector, capabilities.inlayHintsProvider);
62+
if (!id || !options) {
63+
return;
64+
}
65+
this.register({ id: id, registerOptions: options });
66+
}
67+
68+
protected registerLanguageProvider(options: Proposed.InlayHintsRegistrationOptions): [Disposable, InlayHintsProvider] {
69+
const provider: InlayHintsProvider = {
70+
provideInlayHints: (document, range, token) => {
71+
const client = this._client;
72+
const provideInlayHints: ProvideInlayHintsSignature = (document, range, token) => {
73+
const requestParams: Proposed.InlayHintsParams = {
74+
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
75+
range: client.code2ProtocolConverter.asRange(range)
76+
};
77+
return client.sendRequest(Proposed.InlayHintsRequest.type, requestParams, token).then(
78+
(m: Proposed.InlayHint[]) => m.map(h => protocol2code.asInlayHint(client.protocol2CodeConverter, h)),
79+
(error: any) => {
80+
return client.handleFailedRequest(Proposed.InlayHintsRequest.type, token, error, null);
81+
}
82+
);
83+
};
84+
const middleware = client.clientOptions.middleware as (Middleware & InlayHintsMiddleware) | undefined;
85+
return middleware?.provideInlayHints
86+
? middleware.provideInlayHints(document, range, token, provideInlayHints)
87+
: provideInlayHints(document, range, token);
88+
}
89+
};
90+
return [Languages.registerInlayHintsProvider(options.documentSelector!, provider), provider];
91+
}
92+
}

client/typings/vscode-proposed.d.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,85 @@ declare module 'vscode' {
4545
// todo@API proper event type
4646
export const onDidChangeOpenEditors: Event<void>;
4747
}
48+
49+
//#region https://github.com/microsoft/vscode/issues/16221
50+
51+
// todo@API Split between Inlay- and OverlayHints (InlayHint are for a position, OverlayHints for a non-empty range)
52+
// todo@API add "mini-markdown" for links and styles
53+
// (done) remove description
54+
// (done) rename to InlayHint
55+
// (done) add InlayHintKind with type, argument, etc
56+
57+
export namespace languages {
58+
/**
59+
* Register a inlay hints provider.
60+
*
61+
* Multiple providers can be registered for a language. In that case providers are asked in
62+
* parallel and the results are merged. A failing provider (rejected promise or exception) will
63+
* not cause a failure of the whole operation.
64+
*
65+
* @param selector A selector that defines the documents this provider is applicable to.
66+
* @param provider An inlay hints provider.
67+
* @return A {@link Disposable} that unregisters this provider when being disposed.
68+
*/
69+
export function registerInlayHintsProvider(selector: DocumentSelector, provider: InlayHintsProvider): Disposable;
70+
}
71+
72+
export enum InlayHintKind {
73+
Other = 0,
74+
Type = 1,
75+
Parameter = 2,
76+
}
77+
78+
/**
79+
* Inlay hint information.
80+
*/
81+
export class InlayHint {
82+
/**
83+
* The text of the hint.
84+
*/
85+
text: string;
86+
/**
87+
* The position of this hint.
88+
*/
89+
position: Position;
90+
/**
91+
* The kind of this hint.
92+
*/
93+
kind?: InlayHintKind;
94+
/**
95+
* Whitespace before the hint.
96+
*/
97+
whitespaceBefore?: boolean;
98+
/**
99+
* Whitespace after the hint.
100+
*/
101+
whitespaceAfter?: boolean;
102+
103+
// todo@API make range first argument
104+
constructor(text: string, position: Position, kind?: InlayHintKind);
105+
}
106+
107+
/**
108+
* The inlay hints provider interface defines the contract between extensions and
109+
* the inlay hints feature.
110+
*/
111+
export interface InlayHintsProvider {
112+
113+
/**
114+
* An optional event to signal that inlay hints have changed.
115+
* @see {@link EventEmitter}
116+
*/
117+
onDidChangeInlayHints?: Event<void>;
118+
119+
/**
120+
*
121+
* @param model The document in which the command was invoked.
122+
* @param range The range for which inlay hints should be computed.
123+
* @param token A cancellation token.
124+
* @return A list of inlay hints or a thenable that resolves to such.
125+
*/
126+
provideInlayHints(model: TextDocument, range: Range, token: CancellationToken): ProviderResult<InlayHint[]>;
127+
}
128+
//#endregion
48129
}

protocol/src/common/api.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,19 @@ export namespace Proposed {
8888
export type WorkspaceDiagnosticReport = diag.WorkspaceDiagnosticReport;
8989
export type WorkspaceDiagnosticReportPartialResult = diag.WorkspaceDiagnosticReportPartialResult;
9090
export const DiagnosticRefreshRequest: typeof diag.DiagnosticRefreshRequest = diag.DiagnosticRefreshRequest;
91+
}
92+
93+
import * as inlay from './proposed.inlayHints';
94+
95+
export namespace Proposed {
96+
export type InlayHintCategory = inlay.InlayHintCategory;
97+
export const InlayHintCategory = inlay.InlayHintCategory;
98+
export type InlayHint = inlay.InlayHint;
99+
export type InlayHintsClientCapabilities = inlay.InlayHintsClientCapabilities;
100+
export type InlayHintsOptions = inlay.InlayHintsOptions;
101+
export type InlayHintsRegistrationOptions = inlay.InlayHintsRegistrationOptions;
102+
export type $InlayHintsClientCapabilities = inlay.$InlayHintsClientCapabilities;
103+
export type $InlayHintsServerCapabilities = inlay.$InlayHintsServerCapabilities;
104+
export type InlayHintsParams = inlay.InlayHintsParams;
105+
export const InlayHintsRequest: typeof inlay.InlayHintsRequest = inlay.InlayHintsRequest;
91106
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/* --------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
* ------------------------------------------------------------------------------------------ */
5+
6+
import { ProtocolRequestType } from './messages';
7+
import { Position, Range, TextDocumentIdentifier } from 'vscode-languageserver-types';
8+
import {
9+
WorkDoneProgressOptions, WorkDoneProgressParams, PartialResultParams, TextDocumentRegistrationOptions, TextDocumentClientCapabilities
10+
} from './protocol';
11+
12+
/**
13+
* Well-known kinds of information conveyed by InlayHints.
14+
* Clients may choose which categories to display according to user preferences.
15+
*
16+
* @since 3.17.0
17+
*/
18+
export enum InlayHintCategory {
19+
/**
20+
* The range is an expression passed as an argument to a function.
21+
* The label is the name of the parameter.
22+
*/
23+
Parameter = 'parameter',
24+
/**
25+
* The range is an entity whose type is unknown.
26+
* The label is its inferred type.
27+
*/
28+
Type = 'type'
29+
}
30+
31+
/**
32+
* An inlay hint is a short textual annotation for a range of source code.
33+
*
34+
* @since 3.17.0
35+
*/
36+
export interface InlayHint {
37+
/**
38+
* The text to be shown.
39+
*/
40+
label: string;
41+
42+
/**
43+
* The position within the code this hint is attached to.
44+
*/
45+
position: Position;
46+
47+
/**
48+
* The kind of information this hint conveys.
49+
* May be an InlayHintCategory or any other value, clients should treat
50+
* unrecognized values as if missing.
51+
*/
52+
category?: string;
53+
}
54+
55+
56+
/**
57+
* Client capabilities specific to the inlayHints request.
58+
*
59+
* @since 3.17.0
60+
*/
61+
export interface InlayHintsClientCapabilities {
62+
/**
63+
* Whether implementation supports dynamic registration. If this is set to
64+
* `true` the client supports the new `(TextDocumentRegistrationOptions &
65+
* StaticRegistrationOptions)` return value for the corresponding server
66+
* capability as well.
67+
*/
68+
dynamicRegistration?: boolean;
69+
}
70+
71+
export interface $InlayHintsClientCapabilities {
72+
textDocument?: TextDocumentClientCapabilities & {
73+
inlayHints?: InlayHintsClientCapabilities;
74+
}
75+
}
76+
77+
export interface InlayHintsServerCapabilities {
78+
}
79+
80+
export interface InlayHintsOptions extends WorkDoneProgressOptions {
81+
}
82+
83+
export interface InlayHintsRegistrationOptions extends TextDocumentRegistrationOptions, InlayHintsOptions {
84+
}
85+
86+
export interface $InlayHintsServerCapabilities {
87+
inlayHintsProvider?: InlayHintsOptions;
88+
}
89+
90+
export interface InlayHintsParams extends WorkDoneProgressParams, PartialResultParams {
91+
/**
92+
* The text document.
93+
*/
94+
textDocument: TextDocumentIdentifier;
95+
96+
/**
97+
* The range the inlay hints are requested for.
98+
* If unset, returns all hints for the document.
99+
*/
100+
range?: Range;
101+
102+
/**
103+
* The categories of inlay hints that are interesting to the client.
104+
* The client should filter out hints of other categories, so the server may
105+
* skip computing them.
106+
*/
107+
only?: string[];
108+
}
109+
110+
/**
111+
* The `textDocument/inlayHints` request is sent from the client to the server to retrieve inlay hints for a document.
112+
*/
113+
export namespace InlayHintsRequest {
114+
export const method: 'textDocument/inlayHints' = 'textDocument/inlayHints';
115+
export const type = new ProtocolRequestType<InlayHintsParams, InlayHint[], InlayHint[], void, InlayHintsRegistrationOptions>(method);
116+
}

0 commit comments

Comments
 (0)