From 8473b1e6da41e023320b5d426c3dbb77385cd9e5 Mon Sep 17 00:00:00 2001 From: Jackson Schuster Date: Sun, 6 Mar 2022 00:27:02 -0800 Subject: [PATCH 1/7] Add GoToTypeDefinition provider PR https://github.com/OmniSharp/omnisharp-roslyn/pull/2315 add a gototypedefinition endpoint. This PR adds a provider that utilizes the endpoint. --- src/features/typeDefinitionProvider.ts | 98 ++++++++++++++++++++++++++ src/omnisharp/extension.ts | 4 ++ src/omnisharp/prioritization.ts | 3 +- src/omnisharp/protocol.ts | 16 +++++ src/omnisharp/utils.ts | 4 ++ 5 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/features/typeDefinitionProvider.ts diff --git a/src/features/typeDefinitionProvider.ts b/src/features/typeDefinitionProvider.ts new file mode 100644 index 000000000..6d00024a8 --- /dev/null +++ b/src/features/typeDefinitionProvider.ts @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { CancellationToken, TypeDefinitionProvider, Location, Position, TextDocument, Uri } from 'vscode'; +import { MetadataRequest, MetadataSource, GoToTypeDefinitionRequest, } from '../omnisharp/protocol'; +import { createRequest, toRange3, toVscodeLocation } from '../omnisharp/typeConversion'; +import AbstractSupport from './abstractProvider'; +import DefinitionMetadataOrSourceGeneratedDocumentProvider from './definitionMetadataDocumentProvider'; +import { OmniSharpServer } from '../omnisharp/server'; +import { LanguageMiddlewareFeature } from '../omnisharp/LanguageMiddlewareFeature'; +import SourceGeneratedDocumentProvider from './sourceGeneratedDocumentProvider'; + +export default class CSharpTypeDefinitionProvider extends AbstractSupport implements TypeDefinitionProvider { + constructor( + server: OmniSharpServer, + private definitionMetadataDocumentProvider: DefinitionMetadataOrSourceGeneratedDocumentProvider, + private sourceGeneratedDocumentProvider: SourceGeneratedDocumentProvider, + languageMiddlewareFeature: LanguageMiddlewareFeature) { + super(server, languageMiddlewareFeature); + } + + public async provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): Promise { + + let req = createRequest(document, position); + req.WantMetadata = true; + + const locations: Location[] = []; + try { + const goToTypeDefinitionResponse = await serverUtils.goToTypeDefinition(this._server, req, token); + // the defintion is in source + if (goToTypeDefinitionResponse && goToTypeDefinitionResponse.Definitions) { + + for (const definition of goToTypeDefinitionResponse.Definitions) { + if (definition.MetadataSource) { + // the definition is in metadata + const metadataSource: MetadataSource = definition.MetadataSource; + + // Do we already have a document for this metadata reference? + if (definition.Location.FileName.startsWith("$metadata$") && + this.definitionMetadataDocumentProvider.hasMetadataDocument(definition.Location.FileName)) { + + // if it is part of an already used metadata file, retrieve its uri instead of going to the physical file + const uri = this.definitionMetadataDocumentProvider.getExistingMetadataResponseUri(definition.Location.FileName); + const vscodeRange = toRange3(definition.Location.Range); + locations.push(new Location(uri, vscodeRange)); + continue; + } + + // We need to go to the metadata endpoint for more information + const metadataResponse = await serverUtils.getMetadata(this._server, { + Timeout: 5000, + AssemblyName: metadataSource.AssemblyName, + VersionNumber: metadataSource.VersionNumber, + ProjectName: metadataSource.ProjectName, + Language: metadataSource.Language, + TypeName: metadataSource.TypeName + }); + + if (!metadataResponse || !metadataResponse.Source || !metadataResponse.SourceName) { + continue; + } + + const uri: Uri = this.definitionMetadataDocumentProvider.addMetadataResponse(metadataResponse); + const vscodeRange = toRange3(definition.Location.Range); + locations.push(new Location(uri, vscodeRange)); + } else if (definition.SourceGeneratedFileInfo) { + // File is source generated + let uri = this.sourceGeneratedDocumentProvider.tryGetExistingSourceGeneratedFile(definition.SourceGeneratedFileInfo); + if (!uri) { + const sourceGeneratedFileResponse = await serverUtils.getSourceGeneratedFile(this._server, definition.SourceGeneratedFileInfo, token); + + if (!sourceGeneratedFileResponse || !sourceGeneratedFileResponse.Source || !sourceGeneratedFileResponse.SourceName) { + continue; + } + + uri = this.sourceGeneratedDocumentProvider.addSourceGeneratedFile(definition.SourceGeneratedFileInfo, sourceGeneratedFileResponse); + } + + locations.push(new Location(uri, toRange3(definition.Location.Range))); + } else { + // if it is a normal source definition, convert the response to a location + locations.push(toVscodeLocation(definition.Location)); + } + } + } + + // Allow language middlewares to re-map its edits if necessary. + const result = await this._languageMiddlewareFeature.remap("remapLocations", locations, token); + return result; + } + catch (error) { + return []; + } + } +} diff --git a/src/omnisharp/extension.ts b/src/omnisharp/extension.ts index 97505e497..f584ca5e4 100644 --- a/src/omnisharp/extension.ts +++ b/src/omnisharp/extension.ts @@ -14,6 +14,7 @@ import CodeLensProvider from '../features/codeLensProvider'; import CompletionProvider, { CompletionAfterInsertCommand } from '../features/completionProvider'; import DefinitionMetadataDocumentProvider from '../features/definitionMetadataDocumentProvider'; import DefinitionProvider from '../features/definitionProvider'; +import TypeDefinitionProvider from '../features/typeDefinitionProvider'; import DocumentHighlightProvider from '../features/documentHighlightProvider'; import DocumentSymbolProvider from '../features/documentSymbolProvider'; import FormatProvider from '../features/formattingEditProvider'; @@ -80,8 +81,11 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an sourceGeneratedDocumentProvider.register(); localDisposables.add(sourceGeneratedDocumentProvider); const definitionProvider = new DefinitionProvider(server, definitionMetadataDocumentProvider, sourceGeneratedDocumentProvider, languageMiddlewareFeature); + const typeDefinitionProvider = new TypeDefinitionProvider(server, definitionMetadataDocumentProvider, sourceGeneratedDocumentProvider, languageMiddlewareFeature); localDisposables.add(vscode.languages.registerDefinitionProvider(documentSelector, definitionProvider)); localDisposables.add(vscode.languages.registerDefinitionProvider({ scheme: definitionMetadataDocumentProvider.scheme }, definitionProvider)); + localDisposables.add(vscode.languages.registerTypeDefinitionProvider(documentSelector, typeDefinitionProvider)); + localDisposables.add(vscode.languages.registerTypeDefinitionProvider({ scheme: definitionMetadataDocumentProvider.scheme }, typeDefinitionProvider)); localDisposables.add(vscode.languages.registerImplementationProvider(documentSelector, new ImplementationProvider(server, languageMiddlewareFeature))); localDisposables.add(vscode.languages.registerCodeLensProvider(documentSelector, new CodeLensProvider(server, testManager, optionProvider, languageMiddlewareFeature))); localDisposables.add(vscode.languages.registerDocumentHighlightProvider(documentSelector, new DocumentHighlightProvider(server, languageMiddlewareFeature))); diff --git a/src/omnisharp/prioritization.ts b/src/omnisharp/prioritization.ts index 1484947bf..775e17336 100644 --- a/src/omnisharp/prioritization.ts +++ b/src/omnisharp/prioritization.ts @@ -22,7 +22,8 @@ const normalCommands = [ protocol.V2.Requests.GoToDefinition, protocol.Requests.RunCodeAction, protocol.Requests.SignatureHelp, - protocol.Requests.TypeLookup + protocol.Requests.TypeLookup, + protocol.Requests.GoToTypeDefinition ]; const prioritySet = new Set(priorityCommands); diff --git a/src/omnisharp/protocol.ts b/src/omnisharp/protocol.ts index d95c31ea1..098e5d303 100644 --- a/src/omnisharp/protocol.ts +++ b/src/omnisharp/protocol.ts @@ -17,6 +17,7 @@ export module Requests { export const FormatAfterKeystroke = '/formatAfterKeystroke'; export const FormatRange = '/formatRange'; export const GetCodeActions = '/getcodeactions'; + export const GoToTypeDefinition = '/gototypedefinition'; export const FindImplementations = '/findimplementations'; export const Project = '/project'; export const Projects = '/projects'; @@ -578,6 +579,21 @@ export enum UpdateType { export interface SourceGeneratedFileClosedRequest extends SourceGeneratedFileInfo { } +export interface Definition { + Location: V2.Location; + MetadataSource?: MetadataSource; + SourceGeneratedFileInfo?: SourceGeneratedFileInfo; +} + +export interface GoToTypeDefinitionRequest extends Request { + WantMetadata?: boolean; +} + +export interface GoToTypeDefinitionResponse { + Definitions?: Definition[]; +} + + export namespace V2 { export module Requests { diff --git a/src/omnisharp/utils.ts b/src/omnisharp/utils.ts index 8a5186f6b..6a57bcfb0 100644 --- a/src/omnisharp/utils.ts +++ b/src/omnisharp/utils.ts @@ -67,6 +67,10 @@ export async function goToDefinition(server: OmniSharpServer, request: protocol. return server.makeRequest(protocol.V2.Requests.GoToDefinition, request, token); } +export async function goToTypeDefinition(server: OmniSharpServer, request: protocol.GoToTypeDefinitionRequest, token: vscode.CancellationToken) { + return server.makeRequest(protocol.Requests.GoToTypeDefinition, request, token); +} + export async function getSourceGeneratedFile(server: OmniSharpServer, request: protocol.SourceGeneratedFileRequest, token: vscode.CancellationToken) { return server.makeRequest(protocol.Requests.SourceGeneratedFile, request, token); } From 6504e9dd57707beafd0d2caa64096fe0e5716d0a Mon Sep 17 00:00:00 2001 From: Jackson Schuster Date: Sun, 6 Mar 2022 13:58:16 -0800 Subject: [PATCH 2/7] Add tests for GoToTypeDefinition --- .../testAssets/singleCsproj/typeDefinition.cs | 24 +++++++ .../typeDefinitionProvider.test.ts | 68 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 test/integrationTests/testAssets/singleCsproj/typeDefinition.cs create mode 100644 test/integrationTests/typeDefinitionProvider.test.ts diff --git a/test/integrationTests/testAssets/singleCsproj/typeDefinition.cs b/test/integrationTests/testAssets/singleCsproj/typeDefinition.cs new file mode 100644 index 000000000..addee4fde --- /dev/null +++ b/test/integrationTests/testAssets/singleCsproj/typeDefinition.cs @@ -0,0 +1,24 @@ + +using System; + +namespace Test +{ + public class LinkedList + { + public void MyMethod() + { + var linked = new LinkedList(); + var str = "test string"; + var part = new PartialClass(); + Console.WriteLine(str); + } + } + + public partial class PartialClass { + public string Foo {get; set;}; + } + + public partial class PartialClass { + public int Bar {get; set;} + } +} diff --git a/test/integrationTests/typeDefinitionProvider.test.ts b/test/integrationTests/typeDefinitionProvider.test.ts new file mode 100644 index 000000000..7da58011a --- /dev/null +++ b/test/integrationTests/typeDefinitionProvider.test.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- +* 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 CSharpDefinitionProvider from "../../src/features/typeDefinitionProvider"; +import * as path from "path"; +import testAssetWorkspace from "./testAssets/testAssetWorkspace"; +import { expect, should } from "chai"; +import { activateCSharpExtension, isRazorWorkspace, isSlnWithGenerator, restartOmniSharpServer } from './integrationHelpers'; + +suite(`${CSharpDefinitionProvider.name}: ${testAssetWorkspace.description}`, () => { + let fileUri: vscode.Uri; + + suiteSetup(async function () { + should(); + + if (isRazorWorkspace(vscode.workspace) || isSlnWithGenerator(vscode.workspace)) { + this.skip(); + } + + const activation = await activateCSharpExtension(); + await testAssetWorkspace.restore(); + + const fileName = 'typeDefinition.cs'; + const projectDirectory = testAssetWorkspace.projects[0].projectDirectoryPath; + fileUri = vscode.Uri.file(path.join(projectDirectory, fileName)); + await vscode.commands.executeCommand("vscode.open", fileUri); + + await testAssetWorkspace.waitForIdle(activation.eventStream); + }); + + suiteTeardown(async () => { + await testAssetWorkspace.cleanupWorkspace(); + }); + + test("Returns the type definition", async () => { + const definitionList = (await vscode.commands.executeCommand("vscode.executeTypeDefinitionProvider", fileUri, new vscode.Position(9, 18))); + expect(definitionList.length).to.be.equal(1); + expect(definitionList[0]).to.exist; + expect(definitionList[0].uri.path).to.contain("typeDefinition.cs"); + }); + + test("Returns the definition from Metadata", async () => { + const omnisharpConfig = vscode.workspace.getConfiguration('omnisharp'); + await omnisharpConfig.update('enableDecompilationSupport', false, vscode.ConfigurationTarget.Global); + await restartOmniSharpServer(); + + const definitionList = (await vscode.commands.executeCommand("vscode.executeTypeDefinitionProvider", fileUri, new vscode.Position(10, 18))); + expect(definitionList.length).to.be.equal(1); + expect(definitionList[0]).to.exist; + expect(definitionList[0].uri.path).to.contain("[metadata] String.cs"); + }); + + test("Returns multiple definitions for partial types", async () => { + const definitionList = (await vscode.commands.executeCommand("vscode.executeTypeDefinitionProvider", fileUri, new vscode.Position(11, 18))); + expect(definitionList.length).eq(2); + expect(definitionList[0]).to.exist; + expect(definitionList[0].uri.path).to.contain("typeDefinition.cs"); + expect(definitionList[1]).to.exist; + expect(definitionList[1].uri.path).to.contain("typeDefinition.cs"); + }); + + suiteTeardown(async () => { + await testAssetWorkspace.cleanupWorkspace(); + }); +}); From 8a0909427bf554e1ca36cf97c9a0e40ffbc6ce42 Mon Sep 17 00:00:00 2001 From: Jackson Schuster Date: Thu, 10 Mar 2022 20:16:52 -0800 Subject: [PATCH 3/7] Move GoToTypeDefinition logic to DefinitionProvider class to share code --- src/features/definitionProvider.ts | 131 ++++++++++-------- src/features/typeDefinitionProvider.ts | 98 ------------- src/omnisharp/extension.ts | 6 +- .../typeDefinitionProvider.test.ts | 2 +- 4 files changed, 78 insertions(+), 159 deletions(-) delete mode 100644 src/features/typeDefinitionProvider.ts diff --git a/src/features/definitionProvider.ts b/src/features/definitionProvider.ts index 604bf1169..9422ab817 100644 --- a/src/features/definitionProvider.ts +++ b/src/features/definitionProvider.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as serverUtils from '../omnisharp/utils'; -import { CancellationToken, DefinitionProvider, Location, Position, TextDocument, Uri } from 'vscode'; -import { MetadataRequest, MetadataSource, V2 } from '../omnisharp/protocol'; +import { CancellationToken, TypeDefinitionProvider, DefinitionProvider, Location, Position, TextDocument, Uri } from 'vscode'; +import { GoToTypeDefinitionRequest, GoToTypeDefinitionResponse, MetadataRequest, MetadataSource, V2 } from '../omnisharp/protocol'; import { createRequest, toRange3, toVscodeLocation } from '../omnisharp/typeConversion'; import AbstractSupport from './abstractProvider'; import DefinitionMetadataOrSourceGeneratedDocumentProvider from './definitionMetadataDocumentProvider'; @@ -13,7 +13,7 @@ import { OmniSharpServer } from '../omnisharp/server'; import { LanguageMiddlewareFeature } from '../omnisharp/LanguageMiddlewareFeature'; import SourceGeneratedDocumentProvider from './sourceGeneratedDocumentProvider'; -export default class CSharpDefinitionProvider extends AbstractSupport implements DefinitionProvider { +export default class CSharpDefinitionProvider extends AbstractSupport implements DefinitionProvider, TypeDefinitionProvider { constructor( server: OmniSharpServer, private definitionMetadataDocumentProvider: DefinitionMetadataOrSourceGeneratedDocumentProvider, @@ -27,72 +27,91 @@ export default class CSharpDefinitionProvider extends AbstractSupport implements let req = createRequest(document, position); req.WantMetadata = true; - const locations: Location[] = []; try { const gotoDefinitionResponse = await serverUtils.goToDefinition(this._server, req, token); - // the defintion is in source - if (gotoDefinitionResponse && gotoDefinitionResponse.Definitions) { - - for (const definition of gotoDefinitionResponse.Definitions) { - if (definition.MetadataSource) { - // the definition is in metadata - const metadataSource: MetadataSource = definition.MetadataSource; - - // Do we already have a document for this metadata reference? - if (definition.Location.FileName.startsWith("$metadata$") && - this.definitionMetadataDocumentProvider.hasMetadataDocument(definition.Location.FileName)) { - - // if it is part of an already used metadata file, retrieve its uri instead of going to the physical file - const uri = this.definitionMetadataDocumentProvider.getExistingMetadataResponseUri(definition.Location.FileName); - const vscodeRange = toRange3(definition.Location.Range); - locations.push(new Location(uri, vscodeRange)); - continue; - } + const locations = await this.GetLocationsFromResponse(gotoDefinitionResponse, token); + // Allow language middlewares to re-map its edits if necessary. + const result = await this._languageMiddlewareFeature.remap("remapLocations", locations, token); + return result; + } + catch (error) { + return []; + } + } - // We need to go to the metadata endpoint for more information - const metadataResponse = await serverUtils.getMetadata(this._server, { - Timeout: 5000, - AssemblyName: metadataSource.AssemblyName, - VersionNumber: metadataSource.VersionNumber, - ProjectName: metadataSource.ProjectName, - Language: metadataSource.Language, - TypeName: metadataSource.TypeName - }); - - if (!metadataResponse || !metadataResponse.Source || !metadataResponse.SourceName) { - continue; - } + public async provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): Promise { + let req = createRequest(document, position); + req.WantMetadata = true; + + try { + const goToTypeDefinitionResponse = await serverUtils.goToTypeDefinition(this._server, req, token); + const locations = await this.GetLocationsFromResponse(goToTypeDefinitionResponse, token); + // Allow language middlewares to re-map its edits if necessary. + const result = await this._languageMiddlewareFeature.remap("remapLocations", locations, token); + return result; + } + catch (error) { + return []; + } + } + + private async GetLocationsFromResponse (response: GoToTypeDefinitionResponse | V2.GoToDefinitionResponse, token: CancellationToken): Promise + { + let locations: Location[] = []; + if (response && response.Definitions) { + for (const definition of response.Definitions) { + if (definition.MetadataSource) { + // the definition is in metadata + const metadataSource: MetadataSource = definition.MetadataSource; - const uri: Uri = this.definitionMetadataDocumentProvider.addMetadataResponse(metadataResponse); + // Do we already have a document for this metadata reference? + if (definition.Location.FileName.startsWith("$metadata$") && + this.definitionMetadataDocumentProvider.hasMetadataDocument(definition.Location.FileName)) { + + // if it is part of an already used metadata file, retrieve its uri instead of going to the physical file + const uri = this.definitionMetadataDocumentProvider.getExistingMetadataResponseUri(definition.Location.FileName); const vscodeRange = toRange3(definition.Location.Range); locations.push(new Location(uri, vscodeRange)); - } else if (definition.SourceGeneratedFileInfo) { - // File is source generated - let uri = this.sourceGeneratedDocumentProvider.tryGetExistingSourceGeneratedFile(definition.SourceGeneratedFileInfo); - if (!uri) { - const sourceGeneratedFileResponse = await serverUtils.getSourceGeneratedFile(this._server, definition.SourceGeneratedFileInfo, token); + continue; + } + + // We need to go to the metadata endpoint for more information + const metadataResponse = await serverUtils.getMetadata(this._server, { + Timeout: 5000, + AssemblyName: metadataSource.AssemblyName, + VersionNumber: metadataSource.VersionNumber, + ProjectName: metadataSource.ProjectName, + Language: metadataSource.Language, + TypeName: metadataSource.TypeName + }); + + if (!metadataResponse || !metadataResponse.Source || !metadataResponse.SourceName) { + continue; + } - if (!sourceGeneratedFileResponse || !sourceGeneratedFileResponse.Source || !sourceGeneratedFileResponse.SourceName) { - continue; - } + const uri: Uri = this.definitionMetadataDocumentProvider.addMetadataResponse(metadataResponse); + const vscodeRange = toRange3(definition.Location.Range); + locations.push(new Location(uri, vscodeRange)); + } else if (definition.SourceGeneratedFileInfo) { + // File is source generated + let uri = this.sourceGeneratedDocumentProvider.tryGetExistingSourceGeneratedFile(definition.SourceGeneratedFileInfo); + if (!uri) { + const sourceGeneratedFileResponse = await serverUtils.getSourceGeneratedFile(this._server, definition.SourceGeneratedFileInfo, token); - uri = this.sourceGeneratedDocumentProvider.addSourceGeneratedFile(definition.SourceGeneratedFileInfo, sourceGeneratedFileResponse); + if (!sourceGeneratedFileResponse || !sourceGeneratedFileResponse.Source || !sourceGeneratedFileResponse.SourceName) { + continue; } - locations.push(new Location(uri, toRange3(definition.Location.Range))); - } else { - // if it is a normal source definition, convert the response to a location - locations.push(toVscodeLocation(definition.Location)); + uri = this.sourceGeneratedDocumentProvider.addSourceGeneratedFile(definition.SourceGeneratedFileInfo, sourceGeneratedFileResponse); } + + locations.push(new Location(uri, toRange3(definition.Location.Range))); + } else { + // if it is a normal source definition, convert the response to a location + locations.push(toVscodeLocation(definition.Location)); } } - - // Allow language middlewares to re-map its edits if necessary. - const result = await this._languageMiddlewareFeature.remap("remapLocations", locations, token); - return result; - } - catch (error) { - return []; } + return locations; } } diff --git a/src/features/typeDefinitionProvider.ts b/src/features/typeDefinitionProvider.ts deleted file mode 100644 index 6d00024a8..000000000 --- a/src/features/typeDefinitionProvider.ts +++ /dev/null @@ -1,98 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 { CancellationToken, TypeDefinitionProvider, Location, Position, TextDocument, Uri } from 'vscode'; -import { MetadataRequest, MetadataSource, GoToTypeDefinitionRequest, } from '../omnisharp/protocol'; -import { createRequest, toRange3, toVscodeLocation } from '../omnisharp/typeConversion'; -import AbstractSupport from './abstractProvider'; -import DefinitionMetadataOrSourceGeneratedDocumentProvider from './definitionMetadataDocumentProvider'; -import { OmniSharpServer } from '../omnisharp/server'; -import { LanguageMiddlewareFeature } from '../omnisharp/LanguageMiddlewareFeature'; -import SourceGeneratedDocumentProvider from './sourceGeneratedDocumentProvider'; - -export default class CSharpTypeDefinitionProvider extends AbstractSupport implements TypeDefinitionProvider { - constructor( - server: OmniSharpServer, - private definitionMetadataDocumentProvider: DefinitionMetadataOrSourceGeneratedDocumentProvider, - private sourceGeneratedDocumentProvider: SourceGeneratedDocumentProvider, - languageMiddlewareFeature: LanguageMiddlewareFeature) { - super(server, languageMiddlewareFeature); - } - - public async provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): Promise { - - let req = createRequest(document, position); - req.WantMetadata = true; - - const locations: Location[] = []; - try { - const goToTypeDefinitionResponse = await serverUtils.goToTypeDefinition(this._server, req, token); - // the defintion is in source - if (goToTypeDefinitionResponse && goToTypeDefinitionResponse.Definitions) { - - for (const definition of goToTypeDefinitionResponse.Definitions) { - if (definition.MetadataSource) { - // the definition is in metadata - const metadataSource: MetadataSource = definition.MetadataSource; - - // Do we already have a document for this metadata reference? - if (definition.Location.FileName.startsWith("$metadata$") && - this.definitionMetadataDocumentProvider.hasMetadataDocument(definition.Location.FileName)) { - - // if it is part of an already used metadata file, retrieve its uri instead of going to the physical file - const uri = this.definitionMetadataDocumentProvider.getExistingMetadataResponseUri(definition.Location.FileName); - const vscodeRange = toRange3(definition.Location.Range); - locations.push(new Location(uri, vscodeRange)); - continue; - } - - // We need to go to the metadata endpoint for more information - const metadataResponse = await serverUtils.getMetadata(this._server, { - Timeout: 5000, - AssemblyName: metadataSource.AssemblyName, - VersionNumber: metadataSource.VersionNumber, - ProjectName: metadataSource.ProjectName, - Language: metadataSource.Language, - TypeName: metadataSource.TypeName - }); - - if (!metadataResponse || !metadataResponse.Source || !metadataResponse.SourceName) { - continue; - } - - const uri: Uri = this.definitionMetadataDocumentProvider.addMetadataResponse(metadataResponse); - const vscodeRange = toRange3(definition.Location.Range); - locations.push(new Location(uri, vscodeRange)); - } else if (definition.SourceGeneratedFileInfo) { - // File is source generated - let uri = this.sourceGeneratedDocumentProvider.tryGetExistingSourceGeneratedFile(definition.SourceGeneratedFileInfo); - if (!uri) { - const sourceGeneratedFileResponse = await serverUtils.getSourceGeneratedFile(this._server, definition.SourceGeneratedFileInfo, token); - - if (!sourceGeneratedFileResponse || !sourceGeneratedFileResponse.Source || !sourceGeneratedFileResponse.SourceName) { - continue; - } - - uri = this.sourceGeneratedDocumentProvider.addSourceGeneratedFile(definition.SourceGeneratedFileInfo, sourceGeneratedFileResponse); - } - - locations.push(new Location(uri, toRange3(definition.Location.Range))); - } else { - // if it is a normal source definition, convert the response to a location - locations.push(toVscodeLocation(definition.Location)); - } - } - } - - // Allow language middlewares to re-map its edits if necessary. - const result = await this._languageMiddlewareFeature.remap("remapLocations", locations, token); - return result; - } - catch (error) { - return []; - } - } -} diff --git a/src/omnisharp/extension.ts b/src/omnisharp/extension.ts index f584ca5e4..785098409 100644 --- a/src/omnisharp/extension.ts +++ b/src/omnisharp/extension.ts @@ -14,7 +14,6 @@ import CodeLensProvider from '../features/codeLensProvider'; import CompletionProvider, { CompletionAfterInsertCommand } from '../features/completionProvider'; import DefinitionMetadataDocumentProvider from '../features/definitionMetadataDocumentProvider'; import DefinitionProvider from '../features/definitionProvider'; -import TypeDefinitionProvider from '../features/typeDefinitionProvider'; import DocumentHighlightProvider from '../features/documentHighlightProvider'; import DocumentSymbolProvider from '../features/documentSymbolProvider'; import FormatProvider from '../features/formattingEditProvider'; @@ -81,11 +80,10 @@ export async function activate(context: vscode.ExtensionContext, packageJSON: an sourceGeneratedDocumentProvider.register(); localDisposables.add(sourceGeneratedDocumentProvider); const definitionProvider = new DefinitionProvider(server, definitionMetadataDocumentProvider, sourceGeneratedDocumentProvider, languageMiddlewareFeature); - const typeDefinitionProvider = new TypeDefinitionProvider(server, definitionMetadataDocumentProvider, sourceGeneratedDocumentProvider, languageMiddlewareFeature); localDisposables.add(vscode.languages.registerDefinitionProvider(documentSelector, definitionProvider)); localDisposables.add(vscode.languages.registerDefinitionProvider({ scheme: definitionMetadataDocumentProvider.scheme }, definitionProvider)); - localDisposables.add(vscode.languages.registerTypeDefinitionProvider(documentSelector, typeDefinitionProvider)); - localDisposables.add(vscode.languages.registerTypeDefinitionProvider({ scheme: definitionMetadataDocumentProvider.scheme }, typeDefinitionProvider)); + localDisposables.add(vscode.languages.registerTypeDefinitionProvider(documentSelector, definitionProvider)); + localDisposables.add(vscode.languages.registerTypeDefinitionProvider({ scheme: definitionMetadataDocumentProvider.scheme }, definitionProvider)); localDisposables.add(vscode.languages.registerImplementationProvider(documentSelector, new ImplementationProvider(server, languageMiddlewareFeature))); localDisposables.add(vscode.languages.registerCodeLensProvider(documentSelector, new CodeLensProvider(server, testManager, optionProvider, languageMiddlewareFeature))); localDisposables.add(vscode.languages.registerDocumentHighlightProvider(documentSelector, new DocumentHighlightProvider(server, languageMiddlewareFeature))); diff --git a/test/integrationTests/typeDefinitionProvider.test.ts b/test/integrationTests/typeDefinitionProvider.test.ts index 7da58011a..582274c70 100644 --- a/test/integrationTests/typeDefinitionProvider.test.ts +++ b/test/integrationTests/typeDefinitionProvider.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from "vscode"; -import CSharpDefinitionProvider from "../../src/features/typeDefinitionProvider"; +import CSharpDefinitionProvider from "../../src/features/definitionProvider"; import * as path from "path"; import testAssetWorkspace from "./testAssets/testAssetWorkspace"; import { expect, should } from "chai"; From 8ab97a9049cb358877050664f7f76a1b590cd0ab Mon Sep 17 00:00:00 2001 From: Jackson Schuster Date: Thu, 10 Mar 2022 20:25:01 -0800 Subject: [PATCH 4/7] Add remapping location to shared function --- src/features/definitionProvider.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/features/definitionProvider.ts b/src/features/definitionProvider.ts index 9422ab817..0bb5e8e36 100644 --- a/src/features/definitionProvider.ts +++ b/src/features/definitionProvider.ts @@ -29,10 +29,7 @@ export default class CSharpDefinitionProvider extends AbstractSupport implements try { const gotoDefinitionResponse = await serverUtils.goToDefinition(this._server, req, token); - const locations = await this.GetLocationsFromResponse(gotoDefinitionResponse, token); - // Allow language middlewares to re-map its edits if necessary. - const result = await this._languageMiddlewareFeature.remap("remapLocations", locations, token); - return result; + return await this.GetLocationsFromResponse(gotoDefinitionResponse, token); } catch (error) { return []; @@ -45,10 +42,7 @@ export default class CSharpDefinitionProvider extends AbstractSupport implements try { const goToTypeDefinitionResponse = await serverUtils.goToTypeDefinition(this._server, req, token); - const locations = await this.GetLocationsFromResponse(goToTypeDefinitionResponse, token); - // Allow language middlewares to re-map its edits if necessary. - const result = await this._languageMiddlewareFeature.remap("remapLocations", locations, token); - return result; + return await this.GetLocationsFromResponse(goToTypeDefinitionResponse, token); } catch (error) { return []; @@ -112,6 +106,7 @@ export default class CSharpDefinitionProvider extends AbstractSupport implements } } } - return locations; + // Allow language middlewares to re-map its edits if necessary. + return await this._languageMiddlewareFeature.remap("remapLocations", locations, token); } } From 891e0aa6fe57c0d6cdabd8c3889b10ea47e8ca02 Mon Sep 17 00:00:00 2001 From: Jackson Schuster Date: Thu, 10 Mar 2022 20:28:34 -0800 Subject: [PATCH 5/7] remove extra line --- src/features/definitionProvider.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/features/definitionProvider.ts b/src/features/definitionProvider.ts index 0bb5e8e36..1d8e84256 100644 --- a/src/features/definitionProvider.ts +++ b/src/features/definitionProvider.ts @@ -23,7 +23,6 @@ export default class CSharpDefinitionProvider extends AbstractSupport implements } public async provideDefinition(document: TextDocument, position: Position, token: CancellationToken): Promise { - let req = createRequest(document, position); req.WantMetadata = true; From c826c1518a267eeeca3b3607ca6165a7f06539ed Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Wed, 23 Mar 2022 16:52:10 -0700 Subject: [PATCH 6/7] Add typeDefinition file to slnFilterWithCsproj test project --- .../src/app/typeDefinition.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 test/integrationTests/testAssets/slnFilterWithCsproj/src/app/typeDefinition.cs diff --git a/test/integrationTests/testAssets/slnFilterWithCsproj/src/app/typeDefinition.cs b/test/integrationTests/testAssets/slnFilterWithCsproj/src/app/typeDefinition.cs new file mode 100644 index 000000000..addee4fde --- /dev/null +++ b/test/integrationTests/testAssets/slnFilterWithCsproj/src/app/typeDefinition.cs @@ -0,0 +1,24 @@ + +using System; + +namespace Test +{ + public class LinkedList + { + public void MyMethod() + { + var linked = new LinkedList(); + var str = "test string"; + var part = new PartialClass(); + Console.WriteLine(str); + } + } + + public partial class PartialClass { + public string Foo {get; set;}; + } + + public partial class PartialClass { + public int Bar {get; set;} + } +} From e7f7e5588d0d57919518e50fcfd76656aa47b6e7 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Wed, 23 Mar 2022 16:52:49 -0700 Subject: [PATCH 7/7] Add typeDefinition file to slnWithCsproj test project --- .../slnWithCsproj/src/app/typeDefinition.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 test/integrationTests/testAssets/slnWithCsproj/src/app/typeDefinition.cs diff --git a/test/integrationTests/testAssets/slnWithCsproj/src/app/typeDefinition.cs b/test/integrationTests/testAssets/slnWithCsproj/src/app/typeDefinition.cs new file mode 100644 index 000000000..addee4fde --- /dev/null +++ b/test/integrationTests/testAssets/slnWithCsproj/src/app/typeDefinition.cs @@ -0,0 +1,24 @@ + +using System; + +namespace Test +{ + public class LinkedList + { + public void MyMethod() + { + var linked = new LinkedList(); + var str = "test string"; + var part = new PartialClass(); + Console.WriteLine(str); + } + } + + public partial class PartialClass { + public string Foo {get; set;}; + } + + public partial class PartialClass { + public int Bar {get; set;} + } +}