Skip to content

Commit a665cb1

Browse files
Add InlayHintsProvider and Display hints (#9392)
1 parent b29d5e8 commit a665cb1

File tree

5 files changed

+169
-9
lines changed

5 files changed

+169
-9
lines changed

Extension/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
},
1212
"license": "SEE LICENSE IN LICENSE.txt",
1313
"engines": {
14-
"vscode": "^1.63.0"
14+
"vscode": "^1.65.0"
1515
},
1616
"bugs": {
1717
"url": "https://github.com/Microsoft/vscode-cpptools/issues",
@@ -4480,7 +4480,7 @@
44804480
"@types/plist": "^3.0.2",
44814481
"@types/semver": "^7.1.0",
44824482
"@types/tmp": "^0.1.0",
4483-
"@types/vscode": "1.60.0",
4483+
"@types/vscode": "1.65.0",
44844484
"@types/which": "^1.3.2",
44854485
"@types/yauzl": "^2.9.1",
44864486
"@typescript-eslint/eslint-plugin": "^4.31.1",
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/* --------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All Rights Reserved.
3+
* See 'LICENSE' in the project root for license information.
4+
* ------------------------------------------------------------------------------------------ */
5+
import * as vscode from 'vscode';
6+
import { DefaultClient, openFileVersions } from '../client';
7+
import { Position, RequestType } from 'vscode-languageclient';
8+
9+
interface GetInlayHintsParams {
10+
uri: string;
11+
}
12+
13+
enum InlayHintKind {
14+
Type = 0,
15+
Parameter = 1,
16+
}
17+
18+
interface CppInlayHint {
19+
position: Position;
20+
label: string;
21+
inlayHintKind: InlayHintKind;
22+
isValueRef: boolean;
23+
// hasParamName: boolean;
24+
}
25+
26+
interface GetInlayHintsResult {
27+
fileVersion: number;
28+
canceled: boolean;
29+
inlayHints: CppInlayHint[];
30+
}
31+
32+
type InlayHintsCacheEntry = {
33+
FileVersion: number;
34+
TypeHints: vscode.InlayHint[];
35+
ParameterHints: CppInlayHint[];
36+
};
37+
38+
const GetInlayHintsRequest: RequestType<GetInlayHintsParams, GetInlayHintsResult, void, void> =
39+
new RequestType<GetInlayHintsParams, GetInlayHintsResult, void, void>('cpptools/getInlayHints');
40+
41+
export class InlayHintsProvider implements vscode.InlayHintsProvider {
42+
private client: DefaultClient;
43+
public onDidChangeInlayHintsEvent = new vscode.EventEmitter<void>();
44+
public onDidChangeInlayHints?: vscode.Event<void>;
45+
private cache: Map<string, InlayHintsCacheEntry> = new Map<string, InlayHintsCacheEntry>();
46+
47+
constructor(client: DefaultClient) {
48+
this.client = client;
49+
this.onDidChangeInlayHints = this.onDidChangeInlayHintsEvent.event;
50+
}
51+
52+
public async provideInlayHints(document: vscode.TextDocument, range: vscode.Range,
53+
token: vscode.CancellationToken): Promise<vscode.InlayHint[] | undefined> {
54+
await this.client.awaitUntilLanguageClientReady();
55+
const uriString: string = document.uri.toString();
56+
57+
// Get results from cache if available.
58+
const cacheEntry: InlayHintsCacheEntry | undefined = this.cache.get(uriString);
59+
if (cacheEntry?.FileVersion === document.version) {
60+
return this.buildVSCodeHints(cacheEntry);
61+
}
62+
63+
// Get new results from the language server
64+
const params: GetInlayHintsParams = { uri: uriString };
65+
const inlayHintsResult: GetInlayHintsResult = await this.client.languageClient.sendRequest(GetInlayHintsRequest, params, token);
66+
if (!inlayHintsResult.canceled) {
67+
if (inlayHintsResult.fileVersion === openFileVersions.get(uriString)) {
68+
const cacheEntry: InlayHintsCacheEntry = this.createCacheEntry(inlayHintsResult);
69+
this.cache.set(uriString, cacheEntry);
70+
return this.buildVSCodeHints(cacheEntry);
71+
} else {
72+
// Force another request because file versions do not match.
73+
this.onDidChangeInlayHintsEvent.fire();
74+
}
75+
}
76+
return undefined;
77+
}
78+
79+
public invalidateFile(uri: string): void {
80+
this.cache.delete(uri);
81+
this.onDidChangeInlayHintsEvent.fire();
82+
}
83+
84+
private buildVSCodeHints(cacheEntry: InlayHintsCacheEntry): vscode.InlayHint[] {
85+
let result: vscode.InlayHint[] = [];
86+
const showTypeHintsEnabled: boolean = true; // TODO: get value from settings.
87+
const showParamHintsEnabled: boolean = true; // TODO: get value from settings.
88+
if (showTypeHintsEnabled) {
89+
result = result.concat(cacheEntry?.TypeHints);
90+
}
91+
if (showParamHintsEnabled) {
92+
const resolvedParameterHints: vscode.InlayHint[] = this.resolveParameterHints(cacheEntry.ParameterHints);
93+
result = result.concat(resolvedParameterHints);
94+
}
95+
return result;
96+
}
97+
98+
private resolveParameterHints(hints: CppInlayHint[]): vscode.InlayHint[] {
99+
const resolvedHints: vscode.InlayHint[] = [];
100+
const showRefEnabled: boolean = true; // TODO: get from settings
101+
const hideParamNameEnabled: boolean = false; // TODO: get from settings
102+
for (const h of hints) {
103+
// Build parameter label based on settings.
104+
// TODO: remove label if param includes parameter name or in comments.
105+
const paramName: string = (hideParamNameEnabled /* && h.hasParamName*/) ? "" : h.label;
106+
let refString: string = "";
107+
if (showRefEnabled && h.isValueRef) {
108+
refString = (paramName.length > 0) ? "& " : "&";
109+
}
110+
const colonString: string = (paramName.length > 0 || refString.length > 0) ? ":" : "";
111+
const label: string = refString + paramName + colonString;
112+
113+
const inlayHint: vscode.InlayHint = new vscode.InlayHint(
114+
new vscode.Position(h.position.line, h.position.character),
115+
label,
116+
vscode.InlayHintKind.Parameter);
117+
inlayHint.paddingRight = true;
118+
resolvedHints.push(inlayHint);
119+
};
120+
return resolvedHints;
121+
}
122+
123+
private createCacheEntry(inlayHintsResults: GetInlayHintsResult): InlayHintsCacheEntry {
124+
const typeHints: vscode.InlayHint[] = [];
125+
for (const h of inlayHintsResults.inlayHints) {
126+
if (h.inlayHintKind === InlayHintKind.Type) {
127+
const inlayHint: vscode.InlayHint = new vscode.InlayHint(
128+
new vscode.Position(h.position.line, h.position.character),
129+
h.label,
130+
vscode.InlayHintKind.Type);
131+
inlayHint.paddingRight = true;
132+
typeHints.push(inlayHint);
133+
}
134+
}
135+
const paramHints: CppInlayHint[] = inlayHintsResults.inlayHints.filter(h => h.inlayHintKind === InlayHintKind.Parameter);
136+
const cacheEntry: InlayHintsCacheEntry = {
137+
FileVersion: inlayHintsResults.fileVersion,
138+
TypeHints: typeHints,
139+
ParameterHints: paramHints
140+
};
141+
return cacheEntry;
142+
}
143+
}

Extension/src/LanguageServer/client.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import * as path from 'path';
88
import * as vscode from 'vscode';
99

10-
// Importing providers here
10+
// Start provider imports
1111
import { OnTypeFormattingEditProvider } from './Providers/onTypeFormattingEditProvider';
1212
import { FoldingRangeProvider } from './Providers/foldingRangeProvider';
1313
import { SemanticTokensProvider } from './Providers/semanticTokensProvider';
@@ -18,6 +18,7 @@ import { WorkspaceSymbolProvider } from './Providers/workspaceSymbolProvider';
1818
import { RenameProvider } from './Providers/renameProvider';
1919
import { FindAllReferencesProvider } from './Providers/findAllReferencesProvider';
2020
import { CodeActionProvider } from './Providers/codeActionProvider';
21+
import { InlayHintsProvider } from './Providers/inlayHintProvider';
2122
// End provider imports
2223

2324
import { LanguageClient, LanguageClientOptions, ServerOptions, NotificationType, TextDocumentIdentifier, RequestType, ErrorAction, CloseAction, DidOpenTextDocumentParams, Range, Position, DocumentFilter } from 'vscode-languageclient';
@@ -534,6 +535,7 @@ const ShowMessageWindowNotification: NotificationType<ShowMessageWindowParams, v
534535
const ShowWarningNotification: NotificationType<ShowWarningParams, void> = new NotificationType<ShowWarningParams, void>('cpptools/showWarning');
535536
const ReportTextDocumentLanguage: NotificationType<string, void> = new NotificationType<string, void>('cpptools/reportTextDocumentLanguage');
536537
const SemanticTokensChanged: NotificationType<string, void> = new NotificationType<string, void>('cpptools/semanticTokensChanged');
538+
const InlayHintsChanged: NotificationType<string, void> = new NotificationType<string, void>('cpptools/inlayHintsChanged');
537539
const IntelliSenseSetupNotification: NotificationType<IntelliSenseSetup, void> = new NotificationType<IntelliSenseSetup, void>('cpptools/IntelliSenseSetup');
538540
const SetTemporaryTextDocumentLanguageNotification: NotificationType<SetTemporaryTextDocumentLanguageParams, void> = new NotificationType<SetTemporaryTextDocumentLanguageParams, void>('cpptools/setTemporaryTextDocumentLanguage');
539541
const ReportCodeAnalysisProcessedNotification: NotificationType<number, void> = new NotificationType<number, void>('cpptools/reportCodeAnalysisProcessed');
@@ -716,6 +718,8 @@ export class DefaultClient implements Client {
716718
private onTypeFormattingProviderDisposable: vscode.Disposable | undefined;
717719
private codeFoldingProvider: FoldingRangeProvider | undefined;
718720
private codeFoldingProviderDisposable: vscode.Disposable | undefined;
721+
private inlayHintsProvider: InlayHintsProvider | undefined;
722+
private inlayHintsProviderDisposable: vscode.Disposable | undefined;
719723
private semanticTokensProvider: SemanticTokensProvider | undefined;
720724
private semanticTokensProviderDisposable: vscode.Disposable | undefined;
721725
private innerConfiguration?: configs.CppProperties;
@@ -923,6 +927,12 @@ export class DefaultClient implements Client {
923927
this.semanticTokensProvider = new SemanticTokensProvider(this);
924928
this.semanticTokensProviderDisposable = vscode.languages.registerDocumentSemanticTokensProvider(this.documentSelector, this.semanticTokensProvider, this.semanticTokensLegend);
925929
}
930+
// TODO: enable based on settings.
931+
const isInlayHintsEnabled: boolean = true;
932+
if (isInlayHintsEnabled) {
933+
this.inlayHintsProvider = new InlayHintsProvider(this);
934+
this.inlayHintsProviderDisposable = vscode.languages.registerInlayHintsProvider(this.documentSelector, this.inlayHintsProvider);
935+
}
926936
// Listen for messages from the language server.
927937
this.registerNotifications();
928938
} else {
@@ -1626,7 +1636,9 @@ export class DefaultClient implements Client {
16261636
if (this.semanticTokensProvider) {
16271637
this.semanticTokensProvider.invalidateFile(uri);
16281638
}
1629-
1639+
if (this.inlayHintsProvider) {
1640+
this.inlayHintsProvider.invalidateFile(uri);
1641+
}
16301642
openFileVersions.delete(uri);
16311643
}
16321644

@@ -2160,6 +2172,7 @@ export class DefaultClient implements Client {
21602172
this.languageClient.onNotification(ShowWarningNotification, showWarning);
21612173
this.languageClient.onNotification(ReportTextDocumentLanguage, (e) => this.setTextDocumentLanguage(e));
21622174
this.languageClient.onNotification(SemanticTokensChanged, (e) => this.semanticTokensProvider?.invalidateFile(e));
2175+
this.languageClient.onNotification(InlayHintsChanged, (e) => this.inlayHintsProvider?.invalidateFile(e));
21632176
this.languageClient.onNotification(IntelliSenseSetupNotification, (e) => this.logIntellisenseSetupTime(e));
21642177
this.languageClient.onNotification(SetTemporaryTextDocumentLanguageNotification, (e) => this.setTemporaryTextDocumentLanguage(e));
21652178
this.languageClient.onNotification(ReportCodeAnalysisProcessedNotification, (e) => this.updateCodeAnalysisProcessed(e));
@@ -3127,6 +3140,10 @@ export class DefaultClient implements Client {
31273140
this.semanticTokensProviderDisposable.dispose();
31283141
this.semanticTokensProviderDisposable = undefined;
31293142
}
3143+
if (this.inlayHintsProviderDisposable) {
3144+
this.inlayHintsProviderDisposable.dispose();
3145+
this.inlayHintsProviderDisposable = undefined;
3146+
}
31303147
this.model.dispose();
31313148
}
31323149

Extension/src/LanguageServer/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ export function processDelayedDidOpen(document: vscode.TextDocument): boolean {
363363
return false;
364364
}
365365

366-
function onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]): void {
366+
function onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]): void {
367367
// Process delayed didOpen for any visible editors we haven't seen before
368368
editors.forEach(editor => {
369369
if ((editor.document.uri.scheme === "file") && (editor.document.languageId === "c" || editor.document.languageId === "cpp" || editor.document.languageId === "cuda-cpp")) {

Extension/yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,10 +316,10 @@
316316
resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.1.0.tgz#19cf73a7bcf641965485119726397a096f0049bd"
317317
integrity sha512-6IwZ9HzWbCq6XoQWhxLpDjuADodH/MKXRUIDFudvgjcVdjFknvmR+DNsoUeer4XPrEnrZs04Jj+kfV9pFsrhmA==
318318

319-
"@types/vscode@1.60.0":
320-
version "1.60.0"
321-
resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.60.0.tgz#9330c317691b4f53441a18b598768faeeb71618a"
322-
integrity sha512-wZt3VTmzYrgZ0l/3QmEbCq4KAJ71K3/hmMQ/nfpv84oH8e81KKwPEoQ5v8dNCxfHFVJ1JabHKmCvqdYOoVm1Ow==
319+
"@types/vscode@1.65.0":
320+
version "1.65.0"
321+
resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.65.0.tgz#042dd8d93c32ac62cb826cd0fa12376069d1f448"
322+
integrity sha512-wQhExnh2nEzpjDMSKhUvnNmz3ucpd3E+R7wJkOhBNK3No6fG3VUdmVmMOKD0A8NDZDDDiQcLNxe3oGmX5SjJ5w==
323323

324324
"@types/which@^1.3.2":
325325
version "1.3.2"

0 commit comments

Comments
 (0)