dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.04k stars 4.03k forks source link

Goto Definition F# to C# and Vice Versa #8105

Closed cloudRoutine closed 2 years ago

cloudRoutine commented 8 years ago

Hi I work on VisualFSharpPowerTools and I've been trying to figure out how to hook into Rosyln's goto definition service to allow jumping from F# source to a class's definition in a C# project. If this is possible I'd really appreciate it if someone could point me to the part of the codebase related to this functionality. Also if there's a service we could expose through VisualFSharpPowerTools or Visual FSharp Tools if it needs to be directly through the language service I'd be happy to implement it to allow definition jumps from C# source to F# projects.

Pilchie commented 8 years ago

This is definitely something we'd like to get to.

Pilchie commented 7 years ago

See comments in https://github.com/dotnet/roslyn/issues/16511.

jannesiera commented 5 years ago

This would be an awesome feature and is holding us back to start using F# in existing C# projects.

Reading through the visualfsharp github I found these tickets that seem to indicate that the Roslyn team should "use SVsSymbolicNavigationManager" so the visualfsharp team can finish this feature:

https://github.com/Microsoft/visualfsharp/issues/3497 https://github.com/Microsoft/visualfsharp/pull/3357#issuecomment-317261778

Is this correct? Would this be a big change?

CyrusNajmabadi commented 5 years ago

I definitely would not want to those ancient APIs for this purpose. It would be good to define something much more modern and usable for this purpose.

cartermp commented 5 years ago

@CyrusNajmabadi It turns out you recommended them 😄 https://github.com/dotnet/roslyn/issues/16511#issuecomment-272642995

What more modern alternative could be defined? We did a short investigation into what it took to interface with ISymbol and it was kinda rough

CyrusNajmabadi commented 5 years ago

@CyrusNajmabadi It turns out you recommended them 😄 #16511 (comment)

Haha. I'm nothing if not inconsistent :D

What more modern alternative could be defined?

Maybe someting entirely managed. And a proper free threaded, async, cancellable, MEF, interface that simply says: please try to navigate to this definition and return a bool saying if you were able to. maybe something like this:

public interface ISymbolicNavigationService
{
     Task<bool> TryNavigateToSymbol(ISymbol symbol, CancellationToken cancellationToken);
     Task<bool> TryNavigateToSymbol(string rqName, CancellationToken cancellationToken);
}

Tagging @jasonmalinowski for his thoughts here. It really doesn't seem like it would be super difficult to do this.

jasonmalinowski commented 5 years ago

It really doesn't seem like it would be super difficult to do this.

Well, it's easy until you said you wanted it free-threaded, cancellable, and async. :smile:

jannesiera commented 5 years ago

@CyrusNajmabadi @jasonmalinowski is there any way we could make progress on this?

@cartermp am I correct that on the F# side everything is ready to get this feature (jump to definition) to work, and you are just waiting for (something like) ISymbolicNavigationService to be implemented in Roslyn? Can you confirm this?

It would be great if we could realize a quick win here.

CyrusNajmabadi commented 5 years ago

@jasonmalinowski

Well, it's easy until you said you wanted it free-threaded, cancellable, and async.

  1. lol.
  2. woudl that really be that bad? i mean, all we'd be doing is basically is basically just importing people who could respond to that API. And, similarly, we would respond to it as well.
CyrusNajmabadi commented 5 years ago

@jannesiera

It would be great if we could realize a quick win here.

I don't think there is any quick win here. We'd have to define the interface and make sure that both sides could understand each other. For example, i know roslyn can produce rqnames... but i'm not sure if we can consume them and map them to symbols (if so, that would make things much easier).

jasonmalinowski commented 5 years ago

would [free-threaded, cancellable, and async] really be that bad? i mean, all we'd be doing is basically is basically just importing people who could respond to that API. And, similarly, we would respond to it as well.

I guess my observation is the implementation of the API requires things like:

  1. Figuring out which files are open.
  2. Opening files.
  3. Moving the caret around in a file.

All of which today require us calling into other parts of Visual Studio. And those APIs are all UI-thread bound, offer no cancellation and are synchronous.

CyrusNajmabadi commented 5 years ago

Gotcha. I did not think that those parts woudl be under teh purview of this API. This API would just be about the language saying "i can take it from here". i.e. in an async-cancellable manner, it would introspect itself and say "yup! that looks like a symbol from my language (or not)". And it woudl return true/false accordingly. If 'false', nothing left for it to do. If 'true', it would take over and actually do the navigation however it wanted. In this brave-new-world of JTF, it seems like it would just bounce to the UI thread, and tehn navigate however it wanted. But that would not be part of what was represented in this API.

Does that seems reasonable? is it a terrible idea? Thanks!

jannesiera commented 5 years ago

@CyrusNajmabadi if I were to introduce this interface as a pull request, how would I go about this? Happy to contribute but need some guidance.

CyrusNajmabadi commented 5 years ago

I'd probably start with a PR that defines the new interface, then shows how roslyn would both consume it (i.e. notify 3rd parties of a symbol to navigate to) as well as how it would export it (i.e. roslyn could navigate to a symbol on behalf of 3rd party). Let the team look at the interface/impl and make sure nothing is wonky. At the same time, create a PR on F# to show how it could consume/export the same interface (though hve that PR use a copy of hte interface since Roslyn won't actually have published the real thing yet). Once the teams take a look and approve things. Get the interface/impl merged into roslyn. Then, once F# picks up the latest Roslyn bits, move your F# PR over to using the real API.

Does that make sense?

jannesiera commented 5 years ago

@CyrusNajmabadi I was able to set up a testing environment. Currently, I'm hooking into GoToDefinitionHelpers.TryGoToDefinition since I have a symbol available there and from there I trigger ISymbolicNavigationService.

Besides the fact that this seems like a concern not belonging in GoToDefinitionHelpers, is this somewhat how you envisioned this? Any advice woul be welcome.

CyrusNajmabadi commented 5 years ago

Besides the fact that this seems like a concern not belonging in GoToDefinitionHelpers, is this somewhat how you envisioned this? Any advice woul be welcome.

So far so good. It may not be exactly the right location. But it is probably more than good enough to get a ProofOfConcept up and running.

maxinfet commented 5 years ago

I'd probably start with a PR that defines the new interface, then shows how roslyn would both consume it (i.e. notify 3rd parties of a symbol to navigate to) as well as how it would export it (i.e. roslyn could navigate to a symbol on behalf of 3rd party). Let the team look at the interface/impl and make sure nothing is wonky. At the same time, create a PR on F# to show how it could consume/export the same interface (though hve that PR use a copy of hte interface since Roslyn won't actually have published the real thing yet). Once the teams take a look and approve things. Get the interface/impl merged into roslyn. Then, once F# picks up the latest Roslyn bits, move your F# PR over to using the real API.

Does that make sense?

Do you happen to have a document any where with guidelines when working on issues that cross the boundaries between components? This is pretty clear and doesn't seem like it is terribly specific to the process at hand. Would be nice if these guidelines were some where for people trying to jump in. I wanted to investigate go to definition going from xaml to C# and vice versa but was not sure how I would show a proof of concept for it.

Thanks for writing that up, at the very least I plan on keeping it in mind when I look into issues like this in the future.

TysonMN commented 5 years ago

The ability to go to a C# definition from F# code works in VS Code. How does VS Code achieve this? Can Visual Studio do the same without the need for a new interface like ISymbolicNavigationService (as described above)?

cartermp commented 5 years ago

@bender2k14 the F# tools for VS also allow navigating to C# constructs. It's C# --> F# that doesn't work.

dharmaturtle commented 5 years ago

2019-05-24_20-38-12

@cartermp That is true for C# constructs you define in .csprojs you own, but I don't think that is true for C# whose source code you don't have (like from nuget packages).

cartermp commented 5 years ago

@dharmaturtle What you're referring to is a different feature altogether, Go to Metadata. That is unsupported for F# at this time (we have a tracking issue on the F# repo). It's unrelated to C# <-> F# interop.

TysonMN commented 5 years ago

@cartermp Can you provide a link to that issue?

cartermp commented 5 years ago

@bender2k14 https://github.com/dotnet/fsharp/issues/1939

CyrusNajmabadi commented 2 years ago

Dupe of https://github.com/dotnet/fsharp/issues/13882. Roslyn has an API for F# to use (https://github.com/dotnet/roslyn/pull/64475). So it's up to them to add this support.