From f8b5f82dd90354a22839e595a0ae5864c871e001 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Wed, 7 Feb 2024 18:02:07 -0800 Subject: [PATCH] Deserialize URI in override completion before attempting to open the document --- src/lsptoolshost/commands.ts | 3 +- .../completion.integration.test.ts | 83 +++++++++++++++++++ .../slnWithCsproj/src/app/completion.cs | 3 +- .../workspaceDiagnostics.integration.test.ts | 6 +- 4 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 test/integrationTests/completion.integration.test.ts diff --git a/src/lsptoolshost/commands.ts b/src/lsptoolshost/commands.ts index 17e062c54..0185755e2 100644 --- a/src/lsptoolshost/commands.ts +++ b/src/lsptoolshost/commands.ts @@ -106,7 +106,8 @@ async function completionComplexEdit( const componentName = '[roslyn.client.completionComplexEdit]'; // Find TextDocument, opening if needed. - const document = await vscode.workspace.openTextDocument(uriStr); + const uri = UriConverter.deserialize(uriStr); + const document = await vscode.workspace.openTextDocument(uri); if (document === undefined) { outputAndThrow(outputChannel, `${componentName} Can't open document with path: '${uriStr}'`); } diff --git a/test/integrationTests/completion.integration.test.ts b/test/integrationTests/completion.integration.test.ts new file mode 100644 index 000000000..2afdb17ec --- /dev/null +++ b/test/integrationTests/completion.integration.test.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as path from 'path'; +import { describe, beforeAll, beforeEach, afterAll, test, expect } from '@jest/globals'; +import testAssetWorkspace from './testAssets/testAssetWorkspace'; +import { activateCSharpExtension, openFileInWorkspaceAsync } from './integrationHelpers'; + +describe(`[${testAssetWorkspace.description}] Test Completion`, function () { + beforeAll(async function () { + await activateCSharpExtension(); + }); + + beforeEach(async function () { + const fileName = path.join('src', 'app', 'completion.cs'); + await openFileInWorkspaceAsync(fileName); + }); + + afterAll(async () => { + await testAssetWorkspace.cleanupWorkspace(); + }); + + test('Returns completion items', async () => { + const completionList = await getCompletionsAsync(new vscode.Position(8, 12), undefined, 10); + expect(completionList.items.length).toBeGreaterThan(0); + expect(completionList.items.map((item) => item.label)).toContain('Console'); + }); + + test('Resolve adds documentation', async () => { + const completionList = await getCompletionsAsync(new vscode.Position(8, 12), undefined, 10); + const documentation = completionList.items.slice(0, 10).filter((item) => item.documentation); + expect(documentation.length).toEqual(10); + }); + + test('Override completion is applied', async () => { + const completionList = await getCompletionsAsync(new vscode.Position(12, 24), ' ', 10); + expect(completionList.items.length).toBeGreaterThan(0); + const methodOverrideItem = completionList.items.find( + (item) => item.label === 'Method(singleCsproj2.NeedsImport n)' + ); + expect(methodOverrideItem).toBeDefined(); + expect(methodOverrideItem!.kind).toEqual(vscode.CompletionItemKind.Method); + expect(methodOverrideItem!.command).toBeDefined(); + expect(methodOverrideItem!.command!.command).toEqual('roslyn.client.completionComplexEdit'); + + await vscode.commands.executeCommand( + methodOverrideItem!.command!.command, + methodOverrideItem!.command!.arguments![0], + methodOverrideItem!.command!.arguments![1], + methodOverrideItem!.command!.arguments![2], + methodOverrideItem!.command!.arguments![3] + ); + + const usingLine = vscode.window.activeTextEditor!.document.lineAt(1).text; + const methodOverrideLine = vscode.window.activeTextEditor!.document.lineAt(13).text; + const methodOverrideImplLine = vscode.window.activeTextEditor!.document.lineAt(15).text; + expect(usingLine).toContain('using singleCsproj2;'); + expect(methodOverrideLine).toContain('override void Method(NeedsImport n)'); + expect(methodOverrideImplLine).toContain('base.Method(n);'); + }); + + async function getCompletionsAsync( + position: vscode.Position, + triggerCharacter: string | undefined, + completionsToResolve: number + ): Promise { + const activeEditor = vscode.window.activeTextEditor; + if (!activeEditor) { + throw new Error('No active editor'); + } + + return await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + activeEditor.document.uri, + position, + triggerCharacter, + completionsToResolve + ); + } +}); diff --git a/test/integrationTests/testAssets/slnWithCsproj/src/app/completion.cs b/test/integrationTests/testAssets/slnWithCsproj/src/app/completion.cs index ef6fcad72..b2f792687 100644 --- a/test/integrationTests/testAssets/slnWithCsproj/src/app/completion.cs +++ b/test/integrationTests/testAssets/slnWithCsproj/src/app/completion.cs @@ -6,9 +6,10 @@ class Completion : CompletionBase { static void shouldHaveCompletions(string[] args) { + Completion a = new Completion(); } - // override // Trailing space is intentional + public override // Trailing space is intentional } } diff --git a/test/integrationTests/workspaceDiagnostics.integration.test.ts b/test/integrationTests/workspaceDiagnostics.integration.test.ts index 16c377982..88a408f36 100644 --- a/test/integrationTests/workspaceDiagnostics.integration.test.ts +++ b/test/integrationTests/workspaceDiagnostics.integration.test.ts @@ -36,7 +36,7 @@ describe(`[${testAssetWorkspace.description}] Test diagnostics`, function () { .flatMap(([_, diagnostics]) => diagnostics); expect(diagnosticsInDiagnosticsCs).toHaveLength(4); - expect(diagnosticsInCompletionCs).toHaveLength(6); + expect(diagnosticsInCompletionCs).toHaveLength(4); // Compiler diagnostic in diagnostics.cs expect(getCode(diagnosticsInDiagnosticsCs[2])).toBe('CS0219'); @@ -115,7 +115,7 @@ describe(`[${testAssetWorkspace.description}] Test diagnostics`, function () { .flatMap(([_, diagnostics]) => diagnostics); expect(diagnosticsInDiagnosticsCs).toHaveLength(1); - expect(diagnosticsInCompletionCs).toHaveLength(0); + expect(diagnosticsInCompletionCs).toHaveLength(1); expect( diagnosticsInDiagnosticsCs.some((d) => getCode(d).startsWith('IDE') || getCode(d).startsWith('CA')) @@ -144,7 +144,7 @@ describe(`[${testAssetWorkspace.description}] Test diagnostics`, function () { .flatMap(([_, diagnostics]) => diagnostics); expect(diagnosticsInDiagnosticsCs).toHaveLength(1); - expect(diagnosticsInCompletionCs).toHaveLength(0); + expect(diagnosticsInCompletionCs).toHaveLength(1); expect( diagnosticsInDiagnosticsCs.some((d) => getCode(d).startsWith('IDE') || getCode(d).startsWith('CA'))