From 729cc22ea1a09b0894b07cb638545bd19c72cafa Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 18 Apr 2022 20:25:54 -0700 Subject: [PATCH 1/4] Add support for InlayHint.TextEdits --- package-lock.json | 19 +++---- package.json | 4 +- src/features/inlayHintProvider.ts | 19 ++++--- src/omnisharp/protocol.ts | 1 + src/omnisharp/typeConversion.ts | 10 ++++ .../inlayHints.integration.test.ts | 50 ++++++++++++++----- 6 files changed, 72 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index e3626570f..77d706248 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "@types/semver": "5.5.0", "@types/tmp": "0.0.33", "@types/unzipper": "^0.9.1", - "@types/vscode": "1.65.0", + "@types/vscode": "1.66.0", "@types/yauzl": "2.9.1", "archiver": "5.3.0", "chai": "4.3.4", @@ -77,7 +77,7 @@ "webpack-cli": "4.6.0" }, "engines": { - "vscode": "^1.65.0" + "vscode": "^1.66.0" } }, "node_modules/@discoveryjs/json-ext": { @@ -331,10 +331,11 @@ } }, "node_modules/@types/vscode": { - "version": "1.65.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.65.0.tgz", - "integrity": "sha512-wQhExnh2nEzpjDMSKhUvnNmz3ucpd3E+R7wJkOhBNK3No6fG3VUdmVmMOKD0A8NDZDDDiQcLNxe3oGmX5SjJ5w==", - "dev": true + "version": "1.66.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.66.0.tgz", + "integrity": "sha512-ZfJck4M7nrGasfs4A4YbUoxis3Vu24cETw3DERsNYtDZmYSYtk6ljKexKFKhImO/ZmY6ZMsmegu2FPkXoUFImA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/yauzl": { "version": "2.9.1", @@ -9614,9 +9615,9 @@ } }, "@types/vscode": { - "version": "1.65.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.65.0.tgz", - "integrity": "sha512-wQhExnh2nEzpjDMSKhUvnNmz3ucpd3E+R7wJkOhBNK3No6fG3VUdmVmMOKD0A8NDZDDDiQcLNxe3oGmX5SjJ5w==", + "version": "1.66.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.66.0.tgz", + "integrity": "sha512-ZfJck4M7nrGasfs4A4YbUoxis3Vu24cETw3DERsNYtDZmYSYtk6ljKexKFKhImO/ZmY6ZMsmegu2FPkXoUFImA==", "dev": true }, "@types/yauzl": { diff --git a/package.json b/package.json index fabdfee27..7c1a6c123 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "@types/semver": "5.5.0", "@types/tmp": "0.0.33", "@types/unzipper": "^0.9.1", - "@types/vscode": "1.65.0", + "@types/vscode": "1.66.0", "@types/yauzl": "2.9.1", "archiver": "5.3.0", "chai": "4.3.4", @@ -541,7 +541,7 @@ } ], "engines": { - "vscode": "^1.65.0" + "vscode": "^1.66.0" }, "activationEvents": [ "onDebugInitialConfigurations", diff --git a/src/features/inlayHintProvider.ts b/src/features/inlayHintProvider.ts index cfc449cec..fd0a17c01 100644 --- a/src/features/inlayHintProvider.ts +++ b/src/features/inlayHintProvider.ts @@ -8,8 +8,8 @@ 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 { InlayHint, InlayHintRequest, InlayHintResolve as InlayHintResolveRequest, LinePositionSpanTextChange } from '../omnisharp/protocol'; +import { fromVSCodeRange, toVSCodePosition, toVSCodeTextEdit } from '../omnisharp/typeConversion'; import { isVirtualCSharpDocument } from './virtualDocumentTracker'; export default class CSharpInlayHintProvider extends AbstractProvider implements vscode.InlayHintsProvider { @@ -34,7 +34,7 @@ export default class CSharpInlayHintProvider extends AbstractProvider implements if (document.uri.scheme !== "file") { return []; } - + if (isVirtualCSharpDocument(document)) { return []; } @@ -50,7 +50,7 @@ export default class CSharpInlayHintProvider extends AbstractProvider implements const hints = await serverUtils.getInlayHints(this._server, request, token); return hints.InlayHints.map((inlayHint): vscode.InlayHint => { - const mappedHint = this.toVscodeHint(inlayHint); + const mappedHint = this.toVSCodeHint(inlayHint); this._hintsMap.set(mappedHint, inlayHint); return mappedHint; }); @@ -68,17 +68,22 @@ export default class CSharpInlayHintProvider extends AbstractProvider implements try { const result = await serverUtils.resolveInlayHints(this._server, request, token); - return this.toVscodeHint(result); + return this.toVSCodeHint(result); } catch (error) { return Promise.reject(`Problem invoking 'ResolveInlayHints' on OmniSharpServer: ${error}`); } } - private toVscodeHint(inlayHint: InlayHint): vscode.InlayHint { + private toVSCodeHint(inlayHint: InlayHint): vscode.InlayHint { return { label: inlayHint.Label, position: toVSCodePosition(inlayHint.Position), - tooltip: new vscode.MarkdownString(inlayHint.Tooltip ?? "") + tooltip: new vscode.MarkdownString(inlayHint.Tooltip ?? ""), + textEdits: toVSCodeTextEdits(inlayHint.TextEdits), }; + + function toVSCodeTextEdits(textEdits: LinePositionSpanTextChange[]): vscode.TextEdit[] { + return textEdits ? textEdits.map(toVSCodeTextEdit) : undefined; + } } } diff --git a/src/omnisharp/protocol.ts b/src/omnisharp/protocol.ts index 977a8991c..0fb150178 100644 --- a/src/omnisharp/protocol.ts +++ b/src/omnisharp/protocol.ts @@ -592,6 +592,7 @@ export interface InlayHint { Label: string; Tooltip?: string; Data: any; + TextEdits?: LinePositionSpanTextChange[]; } export interface InlayHintResponse { diff --git a/src/omnisharp/typeConversion.ts b/src/omnisharp/typeConversion.ts index f6b183c94..18d5a3767 100644 --- a/src/omnisharp/typeConversion.ts +++ b/src/omnisharp/typeConversion.ts @@ -62,6 +62,16 @@ export function toVSCodePosition(point: protocol.V2.Point): vscode.Position { return new vscode.Position(point.Line, point.Column); } +export function toVSCodeTextEdit(textChange: protocol.LinePositionSpanTextChange): vscode.TextEdit { + return new vscode.TextEdit(toVSCodeRange(textChange), textChange.NewText); + + function toVSCodeRange(textChange: protocol.LinePositionSpanTextChange): vscode.Range { + const newStart = new vscode.Position(textChange.StartLine, textChange.StartColumn); + const newEnd = new vscode.Position(textChange.EndLine, textChange.EndColumn); + return new vscode.Range(newStart, newEnd); + } +} + export function createRequest(document: vscode.TextDocument, where: vscode.Position | vscode.Range, includeBuffer: boolean = false): T { let Line: number, Column: number; diff --git a/test/integrationTests/inlayHints.integration.test.ts b/test/integrationTests/inlayHints.integration.test.ts index 455fec069..28557e613 100644 --- a/test/integrationTests/inlayHints.integration.test.ts +++ b/test/integrationTests/inlayHints.integration.test.ts @@ -9,6 +9,7 @@ import { should, assert } from 'chai'; import { activateCSharpExtension, isRazorWorkspace, isSlnWithGenerator, restartOmniSharpServer } from './integrationHelpers'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; import * as path from 'path'; +import { InlayHint, LinePositionSpanTextChange } from '../../src/omnisharp/protocol'; const chai = require('chai'); chai.use(require('chai-arrays')); @@ -49,7 +50,7 @@ suite(`Inlay Hints ${testAssetWorkspace.description}`, function () { const projectDirectory = testAssetWorkspace.projects[0].projectDirectoryPath; const filePath = path.join(projectDirectory, fileName); fileUri = vscode.Uri.file(filePath); - + await vscode.commands.executeCommand("vscode.open", fileUri); await testAssetWorkspace.waitForIdle(activation.eventStream); }); @@ -60,19 +61,42 @@ suite(`Inlay Hints ${testAssetWorkspace.description}`, function () { test("Hints retrieved for region", async () => { const range = new vscode.Range(new vscode.Position(4, 8), new vscode.Position(15, 85)); - const hints : vscode.InlayHint[] = await vscode.commands.executeCommand('vscode.executeInlayHintProvider', fileUri, range); + const hints: vscode.InlayHint[] = await vscode.commands.executeCommand('vscode.executeInlayHintProvider', fileUri, range); + assert.lengthOf(hints, 6); - assertValues(hints[0], 'InlayHints ', 6, 12); - assertValues(hints[1], ' InlayHints', 7, 27); - assertValues(hints[2], 'string ', 8, 28); - assertValues(hints[3], 'i: ', 9, 17); - assertValues(hints[4], 'param1: ', 10, 15); - assertValues(hints[5], 'param1: ', 11, 27); - - function assertValues(hint: vscode.InlayHint, expectedLabel: string, expectedLine: number, expectedCharacter: number) { - assert.equal(hint.label, expectedLabel); - assert.equal(hint.position.line, expectedLine); - assert.equal(hint.position.character, expectedCharacter); + + assertInlayHintEqual(hints[0], { Label: 'InlayHints ', Position: { Line: 6, Column: 12 }, Data: {}, TextEdits: [{ StartLine: 6, StartColumn: 8, EndLine: 6, EndColumn: 11, NewText: 'InlayHints' }] }); + assertInlayHintEqual(hints[1], { Label: ' InlayHints', Position: { Line: 7, Column: 27 }, Data: {}, TextEdits: [{ StartLine: 7, StartColumn: 27, EndLine: 7, EndColumn: 27, NewText: ' InlayHints' }] }); + assertInlayHintEqual(hints[2], { Label: 'string ', Position: { Line: 8, Column: 28 }, Data: {}, TextEdits: [{ StartLine: 8, StartColumn: 28, EndLine: 8, EndColumn: 28, NewText: 'string ' }] }); + assertInlayHintEqual(hints[3], { Label: 'i: ', Position: { Line: 9, Column: 17 }, Data: {}, TextEdits: [{ StartLine: 9, StartColumn: 17, EndLine: 9, EndColumn: 17, NewText: 'i: ' }] }); + assertInlayHintEqual(hints[4], { Label: 'param1: ', Position: { Line: 10, Column: 15 }, Data: {}, TextEdits: [{ StartLine: 10, StartColumn: 15, EndLine: 10, EndColumn: 15, NewText: 'param1: ' }] }); + assertInlayHintEqual(hints[5], { Label: 'param1: ', Position: { Line: 11, Column: 27 }, Data: {}, TextEdits: [{ StartLine: 11, StartColumn: 27, EndLine: 11, EndColumn: 27, NewText: 'param1: ' }] }); + + function assertInlayHintEqual(actual: vscode.InlayHint, expected: InlayHint) { + assert.equal(actual.label, expected.Label); + assert.equal(actual.position.line, expected.Position.Line); + assert.equal(actual.position.character, expected.Position.Column); + + if (!actual.textEdits) { + assert.isUndefined(expected.TextEdits); + return; + } + + assert.equal(actual.textEdits.length, expected.TextEdits.length); + for (let i = 0; i < actual.textEdits.length; i++) { + const actualTextEdit = actual.textEdits[i]; + const expectedTextEdit = expected.TextEdits[i]; + + assertTextEditEqual(actualTextEdit, expectedTextEdit); + } + } + + function assertTextEditEqual(actual: vscode.TextEdit, expected: LinePositionSpanTextChange) { + assert.equal(actual.range.start.line, expected.StartLine); + assert.equal(actual.range.start.character, expected.StartColumn); + assert.equal(actual.range.end.line, expected.EndLine); + assert.equal(actual.range.end.character, expected.EndColumn); + assert.equal(actual.newText, expected.NewText); } }); }); From ed352248c09fa57cfb2c2c0b04874fd122a26538 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Mon, 18 Apr 2022 21:21:28 -0700 Subject: [PATCH 2/4] Run integration tests on latest VSCode --- .github/workflows/ci.yml | 2 +- test/runFeatureTests.ts | 2 +- test/runIntegrationTests.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9801a0c42..05da4badc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: gulp test npm run test:artifacts env: - CODE_VERSION: 1.65.0 + CODE_VERSION: 1.66.0 DISPLAY: :99.0 - name: Build platform-specific extension package diff --git a/test/runFeatureTests.ts b/test/runFeatureTests.ts index bbc364717..1d2a9be73 100644 --- a/test/runFeatureTests.ts +++ b/test/runFeatureTests.ts @@ -18,7 +18,7 @@ async function main() { const extensionTestsPath = path.resolve(__dirname, './featureTests/index'); // Download VS Code, unzip it and run the integration test - await runTests({ version: "1.65.0", extensionDevelopmentPath, extensionTestsPath, launchArgs: ['-n', '--verbose'] }); + await runTests({ extensionDevelopmentPath, extensionTestsPath, launchArgs: ['-n', '--verbose'] }); } catch (err) { console.error(err); console.error('Failed to run tests'); diff --git a/test/runIntegrationTests.ts b/test/runIntegrationTests.ts index 3bdd3ddc6..1257cea72 100644 --- a/test/runIntegrationTests.ts +++ b/test/runIntegrationTests.ts @@ -44,7 +44,7 @@ async function main() { } // Download VS Code, unzip it and run the integration test - await runTests({ version: "1.65.0", extensionDevelopmentPath, extensionTestsPath, launchArgs: [workspacePath, '-n', '--verbose'], extensionTestsEnv: process.env }); + await runTests({ extensionDevelopmentPath, extensionTestsPath, launchArgs: [workspacePath, '-n', '--verbose'], extensionTestsEnv: process.env }); } catch (err) { console.error(err); console.error('Failed to run tests'); From 2be1c7d230a44a228a5ce97ebb32f4db6946b321 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Fri, 20 May 2022 09:17:42 -0700 Subject: [PATCH 3/4] Update changelog and readme --- CHANGELOG.md | 2 ++ README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae606aa98..fef04c1c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ * coreclr debug configuration should support input variables for envFile ([#5102](https://github.com/OmniSharp/omnisharp-vscode/issues/5102), PR: [#5189](https://github.com/OmniSharp/omnisharp-vscode/pull/5189)) * Fix small spelling mistake (PR: [#5215](https://github.com/OmniSharp/omnisharp-vscode/pull/5215)) * Low-hanging nullable fruit (PR: [#5186](https://github.com/OmniSharp/omnisharp-vscode/pull/5186)) +* Fire a buffer update instead of filechanged when active editor changes ([#5216](https://github.com/OmniSharp/omnisharp-vscode/issues/5216), PR: [#5218](https://github.com/OmniSharp/omnisharp-vscode/pull/5218)) +* Add support for InlayHint.TextEdits (PR: [#5177](https://github.com/OmniSharp/omnisharp-vscode/pull/5177)) * Fix .net6 OmniSharp acquisition on Linux arm64 (PR: [#5172](https://github.com/OmniSharp/omnisharp-vscode/pull/5172)) * Remove project.json reference in debugger.md (PR: [#5210](https://github.com/OmniSharp/omnisharp-vscode/pull/5210)) * Update debugger to 1.24.5 (PR: [#5211](https://github.com/OmniSharp/omnisharp-vscode/pull/5211)) diff --git a/README.md b/README.md index 339406ee9..0189f68d7 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ See issue [#5120](https://github.com/OmniSharp/omnisharp-vscode/issues/5120) for * coreclr debug configuration should support input variables for envFile ([#5102](https://github.com/OmniSharp/omnisharp-vscode/issues/5102), PR: [#5189](https://github.com/OmniSharp/omnisharp-vscode/pull/5189)) * Fix small spelling mistake (PR: [#5215](https://github.com/OmniSharp/omnisharp-vscode/pull/5215)) * Low-hanging nullable fruit (PR: [#5186](https://github.com/OmniSharp/omnisharp-vscode/pull/5186)) +* Fire a buffer update instead of filechanged when active editor changes ([#5216](https://github.com/OmniSharp/omnisharp-vscode/issues/5216), PR: [#5218](https://github.com/OmniSharp/omnisharp-vscode/pull/5218)) +* Add support for InlayHint.TextEdits (PR: [#5177](https://github.com/OmniSharp/omnisharp-vscode/pull/5177)) * Fix .net6 OmniSharp acquisition on Linux arm64 (PR: [#5172](https://github.com/OmniSharp/omnisharp-vscode/pull/5172)) * Remove project.json reference in debugger.md (PR: [#5210](https://github.com/OmniSharp/omnisharp-vscode/pull/5210)) * Update debugger to 1.24.5 (PR: [#5211](https://github.com/OmniSharp/omnisharp-vscode/pull/5211)) From a1496622da67721797a4657ce5fdb49653731f79 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Fri, 20 May 2022 11:47:22 -0700 Subject: [PATCH 4/4] Use toRange2 helper --- src/omnisharp/typeConversion.ts | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/omnisharp/typeConversion.ts b/src/omnisharp/typeConversion.ts index 6b41be5ed..c4708d0df 100644 --- a/src/omnisharp/typeConversion.ts +++ b/src/omnisharp/typeConversion.ts @@ -63,27 +63,10 @@ export function toVSCodePosition(point: protocol.V2.Point): vscode.Position { } export function toVSCodeTextEdit(textChange: protocol.LinePositionSpanTextChange): vscode.TextEdit { - return new vscode.TextEdit(toVSCodeRange(textChange), textChange.NewText); - - function toVSCodeRange(textChange: protocol.LinePositionSpanTextChange): vscode.Range { - const newStart = new vscode.Position(textChange.StartLine, textChange.StartColumn); - const newEnd = new vscode.Position(textChange.EndLine, textChange.EndColumn); - return new vscode.Range(newStart, newEnd); - } + return new vscode.TextEdit(toRange2(textChange), textChange.NewText); } -export function createRequest(document: vscode.TextDocument, where: vscode.Position | vscode.Range, includeBuffer: boolean = false): T { - - let Line: number, Column: number; - - if (where instanceof vscode.Position) { - Line = where.line; - Column = where.character; - } else if (where instanceof vscode.Range) { - Line = where.start.line; - Column = where.start.character; - } - +export function createRequest(document: vscode.TextDocument, where: vscode.Position, includeBuffer: boolean = false): T { // for metadata sources, we need to remove the [metadata] from the filename, and prepend the $metadata$ authority // this is expected by the Omnisharp server to support metadata-to-metadata navigation const fileName = document.uri.scheme === "omnisharp-metadata" ? @@ -93,8 +76,8 @@ export function createRequest(document: vscode.TextD const request: protocol.Request = { FileName: fileName, Buffer: includeBuffer ? document.getText() : undefined, - Line: Line, - Column: Column, + Line: where.line, + Column: where.character, }; return request;