Skip to content

Commit

Permalink
Support inlay hints (#5107)
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
333fred and JoeRobich authored Mar 29, 2022
1 parent 57c3ae8 commit 7689b0d
Show file tree
Hide file tree
Showing 18 changed files with 487 additions and 20 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ jobs:
gulp test
npm run test:artifacts
env:
CODE_VERSION: 1.45.0
CODE_VERSION: 1.65.0
DISPLAY: :99.0

- name: Build platform-specific extension package
run: gulp 'vsix:release:package:platform-specific'
run: gulp 'vsix:release:package:platform-specific'
16 changes: 8 additions & 8 deletions package-lock.json

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

66 changes: 63 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
"@types/semver": "5.5.0",
"@types/tmp": "0.0.33",
"@types/unzipper": "^0.9.1",
"@types/vscode": "1.58.0",
"@types/vscode": "1.65.0",
"@types/yauzl": "2.9.1",
"archiver": "5.3.0",
"chai": "4.3.4",
Expand Down Expand Up @@ -539,7 +539,7 @@
}
],
"engines": {
"vscode": "^1.61.0"
"vscode": "^1.65.0"
},
"activationEvents": [
"onDebugInitialConfigurations",
Expand Down Expand Up @@ -852,6 +852,66 @@
"default": true,
"description": "Shows the OmniSharp log in the Output pane when OmniSharp reports an error."
},
"csharp.inlayHints.parameters.enabled": {
"type": "boolean",
"default": false,
"description": "Display inline parameter name hints"
},
"csharp.inlayHints.parameters.forLiteralParameters": {
"type": "boolean",
"default": false,
"description": "Show hints for literals"
},
"csharp.inlayHints.parameters.forObjectCreationParameters": {
"type": "boolean",
"default": false,
"description": "Show hints for 'new' expressions"
},
"csharp.inlayHints.parameters.forIndexerParameters": {
"type": "boolean",
"default": false,
"description": "Show hints for indexers"
},
"csharp.inlayHints.parameters.forOtherParameters": {
"type": "boolean",
"default": false,
"description": "Show hints for everything else"
},
"csharp.inlayHints.parameters.suppressForParametersThatDifferOnlyBySuffix": {
"type": "boolean",
"default": false,
"description": "Suppress hints when parameter names differ only by suffix"
},
"csharp.inlayHints.parameters.suppressForParametersThatMatchMethodIntent": {
"type": "boolean",
"default": false,
"description": "Suppress hints when parameter name matches the method's intent"
},
"csharp.inlayHints.parameters.suppressForParametersThatMatchArgumentName": {
"type": "boolean",
"default": false,
"description": "Suppress hints when argument matches parameter name"
},
"csharp.inlayHints.types.enabled": {
"type": "boolean",
"default": false,
"description": "Display inline type hints"
},
"csharp.inlayHints.types.forImplicitVariableTypes": {
"type": "boolean",
"default": false,
"description": "Show hints for variables with inferred types"
},
"csharp.inlayHints.types.forLambdaParameterTypes": {
"type": "boolean",
"default": false,
"description": "Show hints for lambda parameter types"
},
"csharp.inlayHints.types.forImplicitObjectCreation": {
"type": "boolean",
"default": false,
"description": "Show hints for implicit object creation"
},
"omnisharp.path": {
"type": [
"string",
Expand Down Expand Up @@ -4011,4 +4071,4 @@
}
]
}
}
}
6 changes: 3 additions & 3 deletions src/features/fileOpenCloseProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ class FileOpenCloseProvider implements IDisposable {
await serverUtils.fileClose(this._server, { FileName: e.fileName });
}

private async _onActiveTextEditorChange(e: vscode.TextEditor) {
if (shouldIgnoreDocument(e.document)) {
private async _onActiveTextEditorChange(e: vscode.TextEditor | undefined) {
if (e === undefined || shouldIgnoreDocument(e.document)) {
return;
}

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

return false;
}
}
79 changes: 79 additions & 0 deletions src/features/inlayHintProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as serverUtils from '../omnisharp/utils';
import * as vscode from 'vscode';
import AbstractProvider from './abstractProvider';
import { OmniSharpServer } from '../omnisharp/server';
import { LanguageMiddlewareFeature } from '../omnisharp/LanguageMiddlewareFeature';
import CompositeDisposable from '../CompositeDisposable';
import { InlayHint, InlayHintRequest, InlayHintResolve as InlayHintResolveRequest } from '../omnisharp/protocol';
import { fromVSCodeRange, toVSCodePosition } from '../omnisharp/typeConversion';
import { isVirtualCSharpDocument } from './virtualDocumentTracker';

export default class CSharpInlayHintProvider extends AbstractProvider implements vscode.InlayHintsProvider {
private readonly _onDidChangeInlayHints = new vscode.EventEmitter<void>();
public readonly onDidChangeInlayHints = this._onDidChangeInlayHints.event;

private readonly _hintsMap = new Map<vscode.InlayHint, InlayHint>();

constructor(server: OmniSharpServer, languageMiddlewareFeature: LanguageMiddlewareFeature) {
super(server, languageMiddlewareFeature);
this.addDisposables(new CompositeDisposable(
this._onDidChangeInlayHints,
vscode.workspace.onDidChangeTextDocument(e => {
if (e.document.languageId === 'csharp') {
this._onDidChangeInlayHints.fire();
}
})));
}

async provideInlayHints(document: vscode.TextDocument, range: vscode.Range, token: vscode.CancellationToken): Promise<vscode.InlayHint[]> {
if (isVirtualCSharpDocument(document)) {
return [];
}

const request: InlayHintRequest = {
Location: {
FileName: document.fileName,
Range: fromVSCodeRange(range)
}
};

try {
const hints = await serverUtils.getInlayHints(this._server, request, token);

return hints.InlayHints.map((inlayHint): vscode.InlayHint => {
const mappedHint = this.toVscodeHint(inlayHint);
this._hintsMap.set(mappedHint, inlayHint);
return mappedHint;
});
} catch (error) {
return Promise.reject(`Problem invoking 'GetInlayHints' on OmniSharpServer: ${error}`);
}
}

async resolveInlayHint?(hint: vscode.InlayHint, token: vscode.CancellationToken): Promise<vscode.InlayHint> {
if (!this._hintsMap.has(hint)) {
return Promise.reject(`Outdated inlay hint was requested to be resolved, aborting.`);
}

const request: InlayHintResolveRequest = { Hint: this._hintsMap.get(hint) };

try {
const result = await serverUtils.resolveInlayHints(this._server, request, token);
return this.toVscodeHint(result);
} catch (error) {
return Promise.reject(`Problem invoking 'ResolveInlayHints' on OmniSharpServer: ${error}`);
}
}

private toVscodeHint(inlayHint: InlayHint): vscode.InlayHint {
return {
label: inlayHint.Label,
position: toVSCodePosition(inlayHint.Position),
tooltip: new vscode.MarkdownString(inlayHint.Tooltip ?? "")
};
}
}
14 changes: 13 additions & 1 deletion src/observers/OptionChangeObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,19 @@ const omniSharpOptions: ReadonlyArray<OptionsKey> = [
"enableAsyncCompletion",
"useModernNet",
"analyzeOpenDocumentsOnly",
"enableRoslynAnalyzers"
"enableRoslynAnalyzers",
"inlayHintsEnableForParameters",
"inlayHintsForLiteralParameters",
"inlayHintsForObjectCreationParameters",
"inlayHintsForIndexerParameters",
"inlayHintsForOtherParameters",
"inlayHintsSuppressForParametersThatDifferOnlyBySuffix",
"inlayHintsSuppressForParametersThatMatchMethodIntent",
"inlayHintsSuppressForParametersThatMatchArgumentName",
"inlayHintsEnableForTypes",
"inlayHintsForImplicitVariableTypes",
"inlayHintsForLambdaParameterTypes",
"inlayHintsForImplicitObjectCreation",
];

function OmniSharpOptionChangeObservable(optionObservable: Observable<Options>): Observable<Options> {
Expand Down
4 changes: 4 additions & 0 deletions src/omnisharp/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import SemanticTokensProvider from '../features/semanticTokensProvider';
import SourceGeneratedDocumentProvider from '../features/sourceGeneratedDocumentProvider';
import { getDecompilationAuthorization } from './decompilationPrompt';
import { OmniSharpDotnetResolver } from './OmniSharpDotnetResolver';
import CSharpInlayHintProvider from '../features/inlayHintProvider';
import fileOpenClose from '../features/fileOpenCloseProvider';

export interface ActivationResult {
Expand Down Expand Up @@ -119,6 +120,9 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an
const semanticTokensProvider = new SemanticTokensProvider(server, optionProvider, languageMiddlewareFeature);
localDisposables.add(vscode.languages.registerDocumentSemanticTokensProvider(documentSelector, semanticTokensProvider, semanticTokensProvider.getLegend()));
localDisposables.add(vscode.languages.registerDocumentRangeSemanticTokensProvider(documentSelector, semanticTokensProvider, semanticTokensProvider.getLegend()));

const inlayHintsProvider = new CSharpInlayHintProvider(server, languageMiddlewareFeature);
localDisposables.add(vscode.languages.registerInlayHintsProvider(documentSelector, inlayHintsProvider));
}));

disposables.add(server.onServerStop(() => {
Expand Down
37 changes: 37 additions & 0 deletions src/omnisharp/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ export class Options {
public enableAsyncCompletion: boolean,
public analyzeOpenDocumentsOnly: boolean,
public useSemanticHighlighting: boolean,
public inlayHintsEnableForParameters: boolean,
public inlayHintsForLiteralParameters: boolean,
public inlayHintsForObjectCreationParameters: boolean,
public inlayHintsForIndexerParameters: boolean,
public inlayHintsForOtherParameters: boolean,
public inlayHintsSuppressForParametersThatDifferOnlyBySuffix: boolean,
public inlayHintsSuppressForParametersThatMatchMethodIntent: boolean,
public inlayHintsSuppressForParametersThatMatchArgumentName: boolean,
public inlayHintsEnableForTypes: boolean,
public inlayHintsForImplicitVariableTypes: boolean,
public inlayHintsForLambdaParameterTypes: boolean,
public inlayHintsForImplicitObjectCreation: boolean,
public razorPluginPath?: string,
public defaultLaunchSolution?: string,
public monoPath?: string,
Expand Down Expand Up @@ -94,6 +106,19 @@ export class Options {

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

const inlayHintsEnableForParameters = csharpConfig.get<boolean>('inlayHints.parameters.enabled', false);
const inlayHintsForLiteralParameters = csharpConfig.get<boolean>('inlayHints.parameters.forLiteralParameters', false);
const inlayHintsForObjectCreationParameters = csharpConfig.get<boolean>('inlayHints.parameters.forObjectCreationParameters', false);
const inlayHintsForIndexerParameters = csharpConfig.get<boolean>('inlayHints.parameters.forIndexerParameters', false);
const inlayHintsForOtherParameters = csharpConfig.get<boolean>('inlayHints.parameters.forOtherParameters', false);
const inlayHintsSuppressForParametersThatDifferOnlyBySuffix = csharpConfig.get<boolean>('inlayHints.parameters.suppressForParametersThatDifferOnlyBySuffix', false);
const inlayHintsSuppressForParametersThatMatchMethodIntent = csharpConfig.get<boolean>('inlayHints.parameters.suppressForParametersThatMatchMethodIntent', false);
const inlayHintsSuppressForParametersThatMatchArgumentName = csharpConfig.get<boolean>('inlayHints.parameters.suppressForParametersThatMatchArgumentName', false);
const inlayHintsEnableForTypes = csharpConfig.get<boolean>('inlayHints.types.enabled', false);
const inlayHintsForImplicitVariableTypes = csharpConfig.get<boolean>('inlayHints.types.forImplicitVariableTypes', false);
const inlayHintsForLambdaParameterTypes = csharpConfig.get<boolean>('inlayHints.types.forLambdaParameterTypes', false);
const inlayHintsForImplicitObjectCreation = csharpConfig.get<boolean>('inlayHints.types.forImplicitObjectCreation', false);

const disableCodeActions = csharpConfig.get<boolean>('disableCodeActions', false);

const disableMSBuildDiagnosticWarning = omnisharpConfig.get<boolean>('disableMSBuildDiagnosticWarning', false);
Expand Down Expand Up @@ -145,6 +170,18 @@ export class Options {
enableAsyncCompletion,
analyzeOpenDocumentsOnly,
useSemanticHighlighting,
inlayHintsEnableForParameters,
inlayHintsForLiteralParameters,
inlayHintsForObjectCreationParameters,
inlayHintsForIndexerParameters,
inlayHintsForOtherParameters,
inlayHintsSuppressForParametersThatDifferOnlyBySuffix,
inlayHintsSuppressForParametersThatMatchMethodIntent,
inlayHintsSuppressForParametersThatMatchArgumentName,
inlayHintsEnableForTypes,
inlayHintsForImplicitVariableTypes,
inlayHintsForLambdaParameterTypes,
inlayHintsForImplicitObjectCreation,
razorPluginPath,
defaultLaunchSolution,
monoPath,
Expand Down
21 changes: 21 additions & 0 deletions src/omnisharp/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export module Requests {
export const SourceGeneratedFile = '/sourcegeneratedfile';
export const UpdateSourceGeneratedFile = '/updatesourcegeneratedfile';
export const SourceGeneratedFileClosed = '/sourcegeneratedfileclosed';
export const InlayHint = '/inlayHint';
export const InlayHintResolve = '/inlayHint/resolve';
export const FileOpen = '/open';
export const FileClose = '/close';
}
Expand Down Expand Up @@ -581,6 +583,25 @@ export enum UpdateType {
export interface SourceGeneratedFileClosedRequest extends SourceGeneratedFileInfo {
}

export interface InlayHintRequest {
Location: V2.Location;
}

export interface InlayHint {
Position: V2.Point;
Label: string;
Tooltip?: string;
Data: any;
}

export interface InlayHintResponse {
InlayHints: InlayHint[];
}

export interface InlayHintResolve {
Hint: InlayHint;
}

export interface Definition {
Location: V2.Location;
MetadataSource?: MetadataSource;
Expand Down
Loading

0 comments on commit 7689b0d

Please sign in to comment.