OmniSharp / omnisharp-server

HTTP wrapper around NRefactory allowing C# editor plugins to be written in any language.
http://www.omnisharp.net
MIT License
380 stars 97 forks source link

Starting up the server without a solution file #81

Open Valloric opened 10 years ago

Valloric commented 10 years ago

Currently, OmniSharpServer always wants to be presented with a solution file before starting up. This is causing difficulty for YCM (and especially ycmd); we can't start the omnisharp server until we see a C# file from the user. This makes writing tests for ycmd difficult and also makes other user-level abstractions much harder.

Ideally, we'd be able to start the server and then give it a path to a solution file as part of a JSON request. Even better, we'd be able to give the path to the solution file as part of any call to /autocomplete or other handlers; if the solution has already been loaded, this extra piece of info does nothing. If it hasn't been loaded, then block the response until the solution is loaded and a response is prepared.

If this could be coupled with the ability to have multiple solutions loaded at the same time, that would be awesome. But that's secondary.

Valloric commented 10 years ago

cc @mispencer @Chiel92

nosami commented 10 years ago

I'm planning on allowing OmniSharpServer to work without any solution file at all. My idea was to add a command line argument to specify the root directory for OmniSharp to scan. I think this might make testing simpler for you as you don't need to have a solution file to hand. I guess this idea could be taken even further with a parameter that starts the server with a single file.

I just got started with vimscript tests (should have done it ages ago). This is my approach https://github.com/nosami/Omnisharp/blob/master/test/ although it is a little kludgey. StartServer.vader polls the server waiting for it to be ready.

I don't like the idea of blocking until the solution has loaded (at least not by default) as some solutions can take a long time to load, but the server can still perform useful actions while it's loading. I have no problem with adding this as a command line parameter though.

I've also been thinking about allowing multiple solutions to be hosted from a single port. I would love if YCM and the OmniSharp client could use the same port. Ideally, it could also use the same server location that the OmniSharp client uses. My main issue with YCM right now is that it loads a separate OmniSharp server up on a separate port to the OmniSharp client (so 2 ASTs, double the memory etc.) This drives me nuts, as I can't use YCM without the OmniSharp client :)

On Sat, May 31, 2014 at 12:08 AM, Val Markovic notifications@github.com wrote:

cc @mispencer https://github.com/mispencer @Chiel92 https://github.com/Chiel92

— Reply to this email directly or view it on GitHub https://github.com/nosami/OmniSharpServer/issues/81#issuecomment-44709072 .

nosami commented 10 years ago

The -s parameter now accepts a folder as well as an sln file.

Valloric commented 10 years ago

The -s parameter now accepts a folder as well as an sln file.

That's a nice feature; thank you for implementing it.

But what YCM really needs is a way to boot the OmniSharpServer without any solution or folder or any knowledge of what the user will be editing (because we don't know at startup) and then tell omni "hey this is the solution the user wants to load." And of course, it would be most excellent if omni could load several solutions at the same time.

Having a blocking call for "load this solution" in omni would be great for YCM; the ycmd server does all the talking to omni and provides a non-blocking interface to the user, but the server itself would loooove a blocking interface. Without it, I have to poll omni to figure out is it ready or not.

nosami commented 10 years ago

Sure, I can do that. I can add a '/loadsolution' call that would block until it's finished loading.

Without waiting for that to finish though, you could call '/autocomplete' and get completions for what's been loaded at that point in time. Aren't some completions better than none? Dependant on what's currently been loaded, you might get the same completions that you would get if you waited for it to finish.

I can understand you wanting it to block for test purposes though.

nosami commented 10 years ago

So you start the OmniSharp server even before a C# file has been edited?

Valloric commented 10 years ago

So you start the OmniSharp server even before a C# file has been edited?

Not currently, but would like to (for tests and other situations).

Without waiting for that to finish though, you could call '/autocomplete' and get completions for what's been loaded at that point in time.

Makes sense.

nosami commented 10 years ago

@sp3ctum, @mispencer @Chiel92, @astralhpi what do you think about this proposal?

I'd like this feature myself, but not too sure which is the best way to proceed. I'd like to weigh up the options before I go any further.

Options :-

  1. Add a /loadsolution call that would load the solution, blocking until it completes.
  2. Add a 'solution' (or folder) parameter onto every request.
  3. Maintain the existing API but detect the solution on the server for every request and automatically load it if it isn't already loaded.

I like option 3. We could remove the solution detection code from each client and make things simpler all around. The only problem with this is if there are multiple solution files found. We'd need a way to handle that.

Another thing to consider is aspVnext http://www.hanselman.com/blog/IntroducingASPNETVNext.aspx. It isn't going to use solution files at all. OmniSharp already works with this providing you have Mono 3.4.1 or Roslyn installed and you start the server using a root folder.

Whichever option is taken, I'll keep the -s parameter but make it optional to maintain backwards compatibility.

Regarding handling multiple solutions, this is already possible using a separate port for each solution. I don't use this myself yet (I stop and start the server if I switch), but that is what the sublime 3 client currently does. It maintains a dictionary of solution files/ports. I think it would be simpler to handle multiple solutions on the server though.

Valloric commented 10 years ago
  1. Add a /loadsolution call that would load the solution, blocking until it completes.

This is fine with me, but read on.

  1. Add a 'solution' (or folder) parameter onto every request.

This one I'm against. It adds undue burden to the client.

  1. Maintain the existing API but detect the solution on the server for every request and automatically load it if it isn't already loaded.

As I've learned from YCM experience and the multitude of pull requests YCM has received to improve automatic solution detection, figuring out the solution for a file isn't easy. I'd love it if that solution-choosing logic could be handed off to OmniSharpServer though; it's the proper place for it. If the clients have to do it, then every client has to duplicate this logic.

I'd love it if the omni version of this logic would be robust to the level that if multiple solutions are present, omni will inspect the solution/project files to determine which solution is the actual parent of a given file. Failure would then be present only if a file belongs to multiple solutions, and that's pretty rare.

I think it would be simpler to handle multiple solutions on the server though.

Yes, this. Having to juggle different ports is a sub-par solution.

mikavilpas commented 10 years ago

Hi, nice idea!

I vote for option 3. My idea would be to support a single solution right away, and possibly later add support for more.

So the way to start OmniSharpServer could be

When the request arrives, have the server figure out which solution the user wants to use.

What do you think?

nosami commented 10 years ago

Yeah..... I made a start on this already. It's quite a lot of work though - not difficult, just a lot of refactoring. Expect it sometime next week.