Skip to content

Commit

Permalink
Support inlay hints
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. Tests still need to be written.
  • Loading branch information
333fred committed Mar 25, 2022
1 parent 57c3ae8 commit 6a40417
Show file tree
Hide file tree
Showing 12 changed files with 312 additions and 15 deletions.
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 @@
}
]
}
}
}
74 changes: 74 additions & 0 deletions src/features/inlayHintProvider.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 * 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';

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[]> {
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",
"enableForParameters",
"forLiteralParameters",
"forObjectCreationParameters",
"forIndexerParameters",
"forOtherParameters",
"suppressForParametersThatDifferOnlyBySuffix",
"suppressForParametersThatMatchMethodIntent",
"suppressForParametersThatMatchArgumentName",
"enableForTypes",
"forImplicitVariableTypes",
"forLambdaParameterTypes",
"forImplicitObjectCreation",
];

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 enableForParameters: boolean,
public forLiteralParameters: boolean,
public forObjectCreationParameters: boolean,
public forIndexerParameters: boolean,
public forOtherParameters: boolean,
public suppressForParametersThatDifferOnlyBySuffix: boolean,
public suppressForParametersThatMatchMethodIntent: boolean,
public suppressForParametersThatMatchArgumentName: boolean,
public enableForTypes: boolean,
public forImplicitVariableTypes: boolean,
public forLambdaParameterTypes: boolean,
public forImplicitObjectCreation: 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 enableForParameters = csharpConfig.get<boolean>('inlayHints.parameters.enabled', false);
const forLiteralParameters = csharpConfig.get<boolean>('inlayHints.parameters.forLiteralParameters', false);
const forObjectCreationParameters = csharpConfig.get<boolean>('inlayHints.parameters.forObjectCreationParameters', false);
const forIndexerParameters = csharpConfig.get<boolean>('inlayHints.parameters.forIndexerParameters', false);
const forOtherParameters = csharpConfig.get<boolean>('inlayHints.parameters.forOtherParameters', false);
const suppressForParametersThatDifferOnlyBySuffix = csharpConfig.get<boolean>('inlayHints.parameters.suppressForParametersThatDifferOnlyBySuffix', false);
const suppressForParametersThatMatchMethodIntent = csharpConfig.get<boolean>('inlayHints.parameters.suppressForParametersThatMatchMethodIntent', false);
const suppressForParametersThatMatchArgumentName = csharpConfig.get<boolean>('inlayHints.parameters.suppressForParametersThatMatchArgumentName', false);
const enableForTypes = csharpConfig.get<boolean>('inlayHints.types.enabled', false);
const forImplicitVariableTypes = csharpConfig.get<boolean>('inlayHints.types.forImplicitVariableTypes', false);
const forLambdaParameterTypes = csharpConfig.get<boolean>('inlayHints.types.forLambdaParameterTypes', false);
const forImplicitObjectCreation = 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,
enableForParameters,
forLiteralParameters,
forObjectCreationParameters,
forIndexerParameters,
forOtherParameters,
suppressForParametersThatDifferOnlyBySuffix,
suppressForParametersThatMatchMethodIntent,
suppressForParametersThatMatchArgumentName,
enableForTypes,
forImplicitVariableTypes,
forLambdaParameterTypes,
forImplicitObjectCreation,
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
18 changes: 18 additions & 0 deletions src/omnisharp/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,24 @@ export class OmniSharpServer {
args.push('RoslynExtensionsOptions:EnableAsyncCompletion=true');
}

if (options.enableForParameters === true) {
args.push(`RoslynExtensionsOptions:InlayHintsOptions:EnableForParameters=${options.enableForParameters.toString()}`);
args.push(`RoslynExtensionsOptions:InlayHintsOptions:ForLiteralParameters=${options.forLiteralParameters.toString()}`);
args.push(`RoslynExtensionsOptions:InlayHintsOptions:ForIndexerParameters=${options.forIndexerParameters.toString()}`);
args.push(`RoslynExtensionsOptions:InlayHintsOptions:ForObjectCreationParameters=${options.forObjectCreationParameters.toString()}`);
args.push(`RoslynExtensionsOptions:InlayHintsOptions:ForOtherParameters=${options.forOtherParameters.toString()}`);
args.push(`RoslynExtensionsOptions:InlayHintsOptions:SuppressForParametersThatDifferOnlyBySuffix=${options.suppressForParametersThatDifferOnlyBySuffix.toString()}`);
args.push(`RoslynExtensionsOptions:InlayHintsOptions:SuppressForParametersThatMatchMethodIntent=${options.suppressForParametersThatMatchMethodIntent.toString()}`);
args.push(`RoslynExtensionsOptions:InlayHintsOptions:SuppressForParametersThatMatchArgumentName=${options.suppressForParametersThatMatchArgumentName.toString()}`);
}

if (options.enableForTypes === true) {
args.push(`RoslynExtensionsOptions:InlayHintsOptions:EnableForTypes=${options.enableForTypes.toString()}`);
args.push(`RoslynExtensionsOptions:InlayHintsOptions:ForImplicitVariableTypes=${options.forImplicitVariableTypes.toString()}`);
args.push(`RoslynExtensionsOptions:InlayHintsOptions:ForLambdaParameterTypes=${options.forLambdaParameterTypes.toString()}`);
args.push(`RoslynExtensionsOptions:InlayHintsOptions:ForImplicitObjectCreation=${options.forImplicitObjectCreation.toString()}`);
}

if (options.analyzeOpenDocumentsOnly === true) {
args.push('RoslynExtensionsOptions:AnalyzeOpenDocumentsOnly=true');
}
Expand Down
15 changes: 15 additions & 0 deletions src/omnisharp/typeConversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ export function toVSCodeRange(StartLine: number, StartColumn: number, EndLine: n
return new vscode.Range(StartLine, StartColumn, EndLine, EndColumn);
}

export function fromVSCodeRange(range: vscode.Range): protocol.V2.Range {
return {
Start: fromVSCodePosition(range.start),
End: fromVSCodePosition(range.end)
};
}

export function fromVSCodePosition(position: vscode.Position): protocol.V2.Point {
return { Line: position.line, Column: position.character };
}

export function toVSCodePosition(point: protocol.V2.Point): vscode.Position {
return new vscode.Position(point.Line, point.Column);
}

export function createRequest<T extends protocol.Request>(document: vscode.TextDocument, where: vscode.Position | vscode.Range, includeBuffer: boolean = false): T {

let Line: number, Column: number;
Expand Down
Loading

0 comments on commit 6a40417

Please sign in to comment.