Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

diff-git #2

Open
Create-For-Fun opened this issue Mar 9, 2022 · 0 comments
Open

diff-git #2

Create-For-Fun opened this issue Mar 9, 2022 · 0 comments

Comments

@Create-For-Fun
Copy link
Owner

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<Location[]> { + + 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.GoToDefinitionResponse>(protocol.V2.Requests.GoToDefinition, request, token); } +export async function goToTypeDefinition(server: OmniSharpServer, request: protocol.GoToTypeDefinitionRequest, token: vscode.CancellationToken) { + return server.makeRequest<protocol.GoToTypeDefinitionResponse>(protocol.Requests.GoToTypeDefinition, request, token); +} + export async function getSourceGeneratedFile(server: OmniSharpServer, request: protocol.SourceGeneratedFileRequest, token: vscode.CancellationToken) { return server.makeRequest<protocol.SourceGeneratedFileResponse>(protocol.Requests.SourceGeneratedFile, request, token); } 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 = <vscode.Location[]>(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 = <vscode.Location[]>(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 = <vscode.Location[]>(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(); + }); +});

Originally posted by @PRACH4 in #1 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant