Skip to content

Commit 7689b0d

Browse files
333fredJoeRobich
andauthored
Support inlay hints (#5107)
Closes OmniSharp/omnisharp-roslyn#1932. Implements the new inlay hint api for vscode, now that it's stable and in LSP. The extension will require 1.65 going forward, as the API for inlay hints was stablized in this release. Co-authored-by: Joey Robichaud <[email protected]>
1 parent 57c3ae8 commit 7689b0d

File tree

18 files changed

+487
-20
lines changed

18 files changed

+487
-20
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ jobs:
4343
gulp test
4444
npm run test:artifacts
4545
env:
46-
CODE_VERSION: 1.45.0
46+
CODE_VERSION: 1.65.0
4747
DISPLAY: :99.0
4848

4949
- name: Build platform-specific extension package
50-
run: gulp 'vsix:release:package:platform-specific'
50+
run: gulp 'vsix:release:package:platform-specific'

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
"@types/semver": "5.5.0",
9494
"@types/tmp": "0.0.33",
9595
"@types/unzipper": "^0.9.1",
96-
"@types/vscode": "1.58.0",
96+
"@types/vscode": "1.65.0",
9797
"@types/yauzl": "2.9.1",
9898
"archiver": "5.3.0",
9999
"chai": "4.3.4",
@@ -539,7 +539,7 @@
539539
}
540540
],
541541
"engines": {
542-
"vscode": "^1.61.0"
542+
"vscode": "^1.65.0"
543543
},
544544
"activationEvents": [
545545
"onDebugInitialConfigurations",
@@ -852,6 +852,66 @@
852852
"default": true,
853853
"description": "Shows the OmniSharp log in the Output pane when OmniSharp reports an error."
854854
},
855+
"csharp.inlayHints.parameters.enabled": {
856+
"type": "boolean",
857+
"default": false,
858+
"description": "Display inline parameter name hints"
859+
},
860+
"csharp.inlayHints.parameters.forLiteralParameters": {
861+
"type": "boolean",
862+
"default": false,
863+
"description": "Show hints for literals"
864+
},
865+
"csharp.inlayHints.parameters.forObjectCreationParameters": {
866+
"type": "boolean",
867+
"default": false,
868+
"description": "Show hints for 'new' expressions"
869+
},
870+
"csharp.inlayHints.parameters.forIndexerParameters": {
871+
"type": "boolean",
872+
"default": false,
873+
"description": "Show hints for indexers"
874+
},
875+
"csharp.inlayHints.parameters.forOtherParameters": {
876+
"type": "boolean",
877+
"default": false,
878+
"description": "Show hints for everything else"
879+
},
880+
"csharp.inlayHints.parameters.suppressForParametersThatDifferOnlyBySuffix": {
881+
"type": "boolean",
882+
"default": false,
883+
"description": "Suppress hints when parameter names differ only by suffix"
884+
},
885+
"csharp.inlayHints.parameters.suppressForParametersThatMatchMethodIntent": {
886+
"type": "boolean",
887+
"default": false,
888+
"description": "Suppress hints when parameter name matches the method's intent"
889+
},
890+
"csharp.inlayHints.parameters.suppressForParametersThatMatchArgumentName": {
891+
"type": "boolean",
892+
"default": false,
893+
"description": "Suppress hints when argument matches parameter name"
894+
},
895+
"csharp.inlayHints.types.enabled": {
896+
"type": "boolean",
897+
"default": false,
898+
"description": "Display inline type hints"
899+
},
900+
"csharp.inlayHints.types.forImplicitVariableTypes": {
901+
"type": "boolean",
902+
"default": false,
903+
"description": "Show hints for variables with inferred types"
904+
},
905+
"csharp.inlayHints.types.forLambdaParameterTypes": {
906+
"type": "boolean",
907+
"default": false,
908+
"description": "Show hints for lambda parameter types"
909+
},
910+
"csharp.inlayHints.types.forImplicitObjectCreation": {
911+
"type": "boolean",
912+
"default": false,
913+
"description": "Show hints for implicit object creation"
914+
},
855915
"omnisharp.path": {
856916
"type": [
857917
"string",
@@ -4011,4 +4071,4 @@
40114071
}
40124072
]
40134073
}
4014-
}
4074+
}

src/features/fileOpenCloseProvider.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ class FileOpenCloseProvider implements IDisposable {
4949
await serverUtils.fileClose(this._server, { FileName: e.fileName });
5050
}
5151

52-
private async _onActiveTextEditorChange(e: vscode.TextEditor) {
53-
if (shouldIgnoreDocument(e.document)) {
52+
private async _onActiveTextEditorChange(e: vscode.TextEditor | undefined) {
53+
if (e === undefined || shouldIgnoreDocument(e.document)) {
5454
return;
5555
}
5656

@@ -71,4 +71,4 @@ function shouldIgnoreDocument(document: vscode.TextDocument) {
7171
}
7272

7373
return false;
74-
}
74+
}

src/features/inlayHintProvider.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
import * as serverUtils from '../omnisharp/utils';
6+
import * as vscode from 'vscode';
7+
import AbstractProvider from './abstractProvider';
8+
import { OmniSharpServer } from '../omnisharp/server';
9+
import { LanguageMiddlewareFeature } from '../omnisharp/LanguageMiddlewareFeature';
10+
import CompositeDisposable from '../CompositeDisposable';
11+
import { InlayHint, InlayHintRequest, InlayHintResolve as InlayHintResolveRequest } from '../omnisharp/protocol';
12+
import { fromVSCodeRange, toVSCodePosition } from '../omnisharp/typeConversion';
13+
import { isVirtualCSharpDocument } from './virtualDocumentTracker';
14+
15+
export default class CSharpInlayHintProvider extends AbstractProvider implements vscode.InlayHintsProvider {
16+
private readonly _onDidChangeInlayHints = new vscode.EventEmitter<void>();
17+
public readonly onDidChangeInlayHints = this._onDidChangeInlayHints.event;
18+
19+
private readonly _hintsMap = new Map<vscode.InlayHint, InlayHint>();
20+
21+
constructor(server: OmniSharpServer, languageMiddlewareFeature: LanguageMiddlewareFeature) {
22+
super(server, languageMiddlewareFeature);
23+
this.addDisposables(new CompositeDisposable(
24+
this._onDidChangeInlayHints,
25+
vscode.workspace.onDidChangeTextDocument(e => {
26+
if (e.document.languageId === 'csharp') {
27+
this._onDidChangeInlayHints.fire();
28+
}
29+
})));
30+
}
31+
32+
async provideInlayHints(document: vscode.TextDocument, range: vscode.Range, token: vscode.CancellationToken): Promise<vscode.InlayHint[]> {
33+
if (isVirtualCSharpDocument(document)) {
34+
return [];
35+
}
36+
37+
const request: InlayHintRequest = {
38+
Location: {
39+
FileName: document.fileName,
40+
Range: fromVSCodeRange(range)
41+
}
42+
};
43+
44+
try {
45+
const hints = await serverUtils.getInlayHints(this._server, request, token);
46+
47+
return hints.InlayHints.map((inlayHint): vscode.InlayHint => {
48+
const mappedHint = this.toVscodeHint(inlayHint);
49+
this._hintsMap.set(mappedHint, inlayHint);
50+
return mappedHint;
51+
});
52+
} catch (error) {
53+
return Promise.reject(`Problem invoking 'GetInlayHints' on OmniSharpServer: ${error}`);
54+
}
55+
}
56+
57+
async resolveInlayHint?(hint: vscode.InlayHint, token: vscode.CancellationToken): Promise<vscode.InlayHint> {
58+
if (!this._hintsMap.has(hint)) {
59+
return Promise.reject(`Outdated inlay hint was requested to be resolved, aborting.`);
60+
}
61+
62+
const request: InlayHintResolveRequest = { Hint: this._hintsMap.get(hint) };
63+
64+
try {
65+
const result = await serverUtils.resolveInlayHints(this._server, request, token);
66+
return this.toVscodeHint(result);
67+
} catch (error) {
68+
return Promise.reject(`Problem invoking 'ResolveInlayHints' on OmniSharpServer: ${error}`);
69+
}
70+
}
71+
72+
private toVscodeHint(inlayHint: InlayHint): vscode.InlayHint {
73+
return {
74+
label: inlayHint.Label,
75+
position: toVSCodePosition(inlayHint.Position),
76+
tooltip: new vscode.MarkdownString(inlayHint.Tooltip ?? "")
77+
};
78+
}
79+
}

src/observers/OptionChangeObserver.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,19 @@ const omniSharpOptions: ReadonlyArray<OptionsKey> = [
2525
"enableAsyncCompletion",
2626
"useModernNet",
2727
"analyzeOpenDocumentsOnly",
28-
"enableRoslynAnalyzers"
28+
"enableRoslynAnalyzers",
29+
"inlayHintsEnableForParameters",
30+
"inlayHintsForLiteralParameters",
31+
"inlayHintsForObjectCreationParameters",
32+
"inlayHintsForIndexerParameters",
33+
"inlayHintsForOtherParameters",
34+
"inlayHintsSuppressForParametersThatDifferOnlyBySuffix",
35+
"inlayHintsSuppressForParametersThatMatchMethodIntent",
36+
"inlayHintsSuppressForParametersThatMatchArgumentName",
37+
"inlayHintsEnableForTypes",
38+
"inlayHintsForImplicitVariableTypes",
39+
"inlayHintsForLambdaParameterTypes",
40+
"inlayHintsForImplicitObjectCreation",
2941
];
3042

3143
function OmniSharpOptionChangeObservable(optionObservable: Observable<Options>): Observable<Options> {

src/omnisharp/extension.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import SemanticTokensProvider from '../features/semanticTokensProvider';
4444
import SourceGeneratedDocumentProvider from '../features/sourceGeneratedDocumentProvider';
4545
import { getDecompilationAuthorization } from './decompilationPrompt';
4646
import { OmniSharpDotnetResolver } from './OmniSharpDotnetResolver';
47+
import CSharpInlayHintProvider from '../features/inlayHintProvider';
4748
import fileOpenClose from '../features/fileOpenCloseProvider';
4849

4950
export interface ActivationResult {
@@ -119,6 +120,9 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an
119120
const semanticTokensProvider = new SemanticTokensProvider(server, optionProvider, languageMiddlewareFeature);
120121
localDisposables.add(vscode.languages.registerDocumentSemanticTokensProvider(documentSelector, semanticTokensProvider, semanticTokensProvider.getLegend()));
121122
localDisposables.add(vscode.languages.registerDocumentRangeSemanticTokensProvider(documentSelector, semanticTokensProvider, semanticTokensProvider.getLegend()));
123+
124+
const inlayHintsProvider = new CSharpInlayHintProvider(server, languageMiddlewareFeature);
125+
localDisposables.add(vscode.languages.registerInlayHintsProvider(documentSelector, inlayHintsProvider));
122126
}));
123127

124128
disposables.add(server.onServerStop(() => {

src/omnisharp/options.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ export class Options {
3636
public enableAsyncCompletion: boolean,
3737
public analyzeOpenDocumentsOnly: boolean,
3838
public useSemanticHighlighting: boolean,
39+
public inlayHintsEnableForParameters: boolean,
40+
public inlayHintsForLiteralParameters: boolean,
41+
public inlayHintsForObjectCreationParameters: boolean,
42+
public inlayHintsForIndexerParameters: boolean,
43+
public inlayHintsForOtherParameters: boolean,
44+
public inlayHintsSuppressForParametersThatDifferOnlyBySuffix: boolean,
45+
public inlayHintsSuppressForParametersThatMatchMethodIntent: boolean,
46+
public inlayHintsSuppressForParametersThatMatchArgumentName: boolean,
47+
public inlayHintsEnableForTypes: boolean,
48+
public inlayHintsForImplicitVariableTypes: boolean,
49+
public inlayHintsForLambdaParameterTypes: boolean,
50+
public inlayHintsForImplicitObjectCreation: boolean,
3951
public razorPluginPath?: string,
4052
public defaultLaunchSolution?: string,
4153
public monoPath?: string,
@@ -94,6 +106,19 @@ export class Options {
94106

95107
const useSemanticHighlighting = csharpConfig.get<boolean>('semanticHighlighting.enabled', false);
96108

109+
const inlayHintsEnableForParameters = csharpConfig.get<boolean>('inlayHints.parameters.enabled', false);
110+
const inlayHintsForLiteralParameters = csharpConfig.get<boolean>('inlayHints.parameters.forLiteralParameters', false);
111+
const inlayHintsForObjectCreationParameters = csharpConfig.get<boolean>('inlayHints.parameters.forObjectCreationParameters', false);
112+
const inlayHintsForIndexerParameters = csharpConfig.get<boolean>('inlayHints.parameters.forIndexerParameters', false);
113+
const inlayHintsForOtherParameters = csharpConfig.get<boolean>('inlayHints.parameters.forOtherParameters', false);
114+
const inlayHintsSuppressForParametersThatDifferOnlyBySuffix = csharpConfig.get<boolean>('inlayHints.parameters.suppressForParametersThatDifferOnlyBySuffix', false);
115+
const inlayHintsSuppressForParametersThatMatchMethodIntent = csharpConfig.get<boolean>('inlayHints.parameters.suppressForParametersThatMatchMethodIntent', false);
116+
const inlayHintsSuppressForParametersThatMatchArgumentName = csharpConfig.get<boolean>('inlayHints.parameters.suppressForParametersThatMatchArgumentName', false);
117+
const inlayHintsEnableForTypes = csharpConfig.get<boolean>('inlayHints.types.enabled', false);
118+
const inlayHintsForImplicitVariableTypes = csharpConfig.get<boolean>('inlayHints.types.forImplicitVariableTypes', false);
119+
const inlayHintsForLambdaParameterTypes = csharpConfig.get<boolean>('inlayHints.types.forLambdaParameterTypes', false);
120+
const inlayHintsForImplicitObjectCreation = csharpConfig.get<boolean>('inlayHints.types.forImplicitObjectCreation', false);
121+
97122
const disableCodeActions = csharpConfig.get<boolean>('disableCodeActions', false);
98123

99124
const disableMSBuildDiagnosticWarning = omnisharpConfig.get<boolean>('disableMSBuildDiagnosticWarning', false);
@@ -145,6 +170,18 @@ export class Options {
145170
enableAsyncCompletion,
146171
analyzeOpenDocumentsOnly,
147172
useSemanticHighlighting,
173+
inlayHintsEnableForParameters,
174+
inlayHintsForLiteralParameters,
175+
inlayHintsForObjectCreationParameters,
176+
inlayHintsForIndexerParameters,
177+
inlayHintsForOtherParameters,
178+
inlayHintsSuppressForParametersThatDifferOnlyBySuffix,
179+
inlayHintsSuppressForParametersThatMatchMethodIntent,
180+
inlayHintsSuppressForParametersThatMatchArgumentName,
181+
inlayHintsEnableForTypes,
182+
inlayHintsForImplicitVariableTypes,
183+
inlayHintsForLambdaParameterTypes,
184+
inlayHintsForImplicitObjectCreation,
148185
razorPluginPath,
149186
defaultLaunchSolution,
150187
monoPath,

src/omnisharp/protocol.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ export module Requests {
3838
export const SourceGeneratedFile = '/sourcegeneratedfile';
3939
export const UpdateSourceGeneratedFile = '/updatesourcegeneratedfile';
4040
export const SourceGeneratedFileClosed = '/sourcegeneratedfileclosed';
41+
export const InlayHint = '/inlayHint';
42+
export const InlayHintResolve = '/inlayHint/resolve';
4143
export const FileOpen = '/open';
4244
export const FileClose = '/close';
4345
}
@@ -581,6 +583,25 @@ export enum UpdateType {
581583
export interface SourceGeneratedFileClosedRequest extends SourceGeneratedFileInfo {
582584
}
583585

586+
export interface InlayHintRequest {
587+
Location: V2.Location;
588+
}
589+
590+
export interface InlayHint {
591+
Position: V2.Point;
592+
Label: string;
593+
Tooltip?: string;
594+
Data: any;
595+
}
596+
597+
export interface InlayHintResponse {
598+
InlayHints: InlayHint[];
599+
}
600+
601+
export interface InlayHintResolve {
602+
Hint: InlayHint;
603+
}
604+
584605
export interface Definition {
585606
Location: V2.Location;
586607
MetadataSource?: MetadataSource;

0 commit comments

Comments
 (0)