Skip to content
This repository was archived by the owner on Sep 21, 2019. It is now read-only.

Conversation

ches
Copy link
Contributor

@ches ches commented Dec 9, 2016

This updates EnsimeLauncher to use the server preinstalled by ensime-sbt 1.12+ if available, as per #353. This should be pretty much ready, just want to get feedback/testing and possibly add some unit tests in the meantime now that the launcher doesn't have to do a bunch of slow side-effecty installation stuff that is obnoxious to test or stub.

I've refactored the launcher to use a strategy pattern for launching/installing with three strategies, in order of precedence:

  1. Assembly jar: for local development or some corporate behind-the-firewall usage.
  2. .ensime: use the build tool-installed server if the .ensime tells us how.
  3. bootstrap server installation: the old way, for transition period until people upgrade ensime-sbt and other build tools add install functionality. It'll be more fun to see the future big red diff that removes SbtBootstrap 😄

One change to note: I removed the ability to specify that a bootstrap installation should use server v2. With the new ensime-sbt an ensimeVersion setting can be given in an ensime.sbt to achieve this much more conveniently. I assume anyone who wants to use server v2 at this point is an ensime-vim developer and you don't mind using sbt and the latest ensime-sbt on your plugin testing project, so it's nice to get rid of this special-case handling.

I think it would be nice to integrate a bit of feedback in the launcher for the user to know what's being used (if their assembly jar is picked up, for instance), either via a logger or in the Vim messages that already announce the server is starting, etc., but IMO that's out of scope for this PR.

if not bootstrap_server and no_classpath:

installed = self.launcher.strategy.isinstalled()
if not installed and not bootstrap_server:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This little strategy.isinstalled() Law of Demeter violation felt dirty for a moment, but I feel the right solution is probably not a delegating method on the launcher itself, but getting this logic entirely out of EnsimeClient as per #325. And likely extending to some of the work toward server lifecycle management.

@@ -7,7 +7,7 @@

from ensime_shared.util import Util

BOOTSTRAPS_ROOT = os.path.join(os.environ['HOME'], '.config/ensime-vim/')
BOOTSTRAPS_ROOT = os.path.join(os.environ['HOME'], '.config', 'ensime-vim')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated, but the config folder location could be improved by using XDG for Linux and AppData for Windows.

Copy link
Contributor

@ktonga ktonga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks really nice. Lemme check it out and try it for a while. Well done!

def using_server_v2(self):
"""Whether user has configured the plugin to use ENSIME v2 protocol."""
"""bool: Whether user has configured the plugin to use ENSIME v2 protocol."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused, keeping this setting just to choose the client version can't lead to a client and server version mismatch?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nvm, it's an undocumented setting so only people knowing what they do should be using it.

Copy link
Contributor

@ktonga ktonga Dec 9, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about the noise, I think we should get rid of this setting and ask the launcher which version we should use for the client.

config = ProjectConfig(config_path)
launcher = EnsimeLauncher(self._vim, config)

if self.using_server_v2:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is a bit of space for improvement here, I think it's not ideal to pick the version of the server and the client separately, so we could ask the launcher which version of the server it is gonna launch. After applying the heuristic to pick the strategy it has all the needed information to answer that question. For assembly jar it can extract the server version from the jar name, for dotensime it has the sbt setting to specify the server version and for bootstrap it's always 1.0 I guess.

What you reckon?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, it isn't ideal to keep a client config setting for this if it's now configurable from the build tools, I hadn't really thought about removing the client setting altogether.

I'm divided about how to go about this, though. Right now the launcher doesn't actually care what version it's launching, and it's kind of nice that it doesn't need to. Parsing it out of jar file names in :ensime-server-jars feels icky to me; it's not otherwise directly available as a field in the .ensime file, and introducing coupling to particular build tools to get it (e.g. asking sbt for ensimeVersion) seems wrong too, at least for this purpose.

IMO what feels right is to get the server version strictly from the handshake messages (ConnectionInfo or whatever might possibly come out of ensime/ensime-server#1552 in the future, it appears that hasn't been resolved yet). That's cleanly agnostic to the launcher strategy.

This implies that handshake is completed before an EnsimeClient gets instantiated for the appropriate protocol version. That would be a change (subject of a new enhancement, if we agree on this design), but it sounds like a sensible one to me. It would get trickier if this becomes async, but how much so is hard to say since those specifics aren't decided/implemented.

Maybe I'm making too big a deal out of avoiding stuff like parsing version out of the jars… clearly it isn't hard to do that, I'm just afraid it leads to multiple launch strategy solutions that are somewhat fragile, and internal coupling that can be avoided—I think there might be conscious reasoning in the fact that server version isn't expressly encoded in .ensime, like that a running server should be the authoritative source of truth for protocol version, not .ensime, nor the build tool, nor a launcher that's finished its job of opaquely invoking java with a classpath.

Thoughts?

If this sounds like the way to go, then I'll create a new ticket for making client protocol selection automatic based on handshake, and g:ensime_server_v2 will be removed once that gets done.

Copy link
Contributor

@ktonga ktonga Dec 11, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you have all valid points, getting the version from the ConnectionInfo sound perfect, even more, we need the protocol version no the server version, it's only that this time internal change to the server were the perfect excuse to also change the protocol, but that could not be the case in the future.

As you said, the problem I see is that handling websocket messages is currently the main responsibility of the client, so we have a chicken and egg problem here due to the current implementation of the client relying on inheritance to pick the protocol version. IMHO the client should be only one, having all the WS machinery and after shacking hands with the server it should instantiate the proper version of protocol handlers.

Go ahead and create the ticket, the change looks big enough to be out of scope of this PR.

Copy link
Contributor Author

@ches ches Dec 16, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay, couldn't spare time during the week—I created #355 and I'm going to try to wrap this PR up with some tests today.

the problem I see is that handling websocket messages is currently the main responsibility of the client, so we have a chicken and egg problem here due to the current implementation of the client relying on inheritance to pick the protocol version. IMHO the client should be only one, having all the WS machinery and after shacking hands with the server it should instantiate the proper version of protocol handlers.

Yep, I'm not sure about what's going to work best here yet either. It'd be nice if responsibility for dealing with connection/messages didn't spread, but it still seems to me like mixins are the most manageable way to deal with protocol versions (open to ideas though). From my POV, I think it will be easier to decide what feels right after knocking out #325 and other server lifecycle aspects that will achieve some clean-up refactoring around these parts of the code.

In my separate ensime-py client library effort I began modeling a Connection as an explicit object—that could end up being something that knows the protocol version for the connection it represents, so maybe that could be a clean way for connections to be started and then passed into the constructor of client instances for subsequent communication. That is a long way from being ready or fully thought out, though 😣

@ktonga
Copy link
Contributor

ktonga commented Dec 17, 2016

Hey @ches I've been trying to give this PR a shot but something is preventing the rplugin from being registered in neovim, I'm on 0.2-dev installed via PPA.
Maybe an exception thrown and not properly handled during startup? Let me know if you cannot reproduce it I can assist you with the diagnosing.

@ches ches force-pushed the dotensime-launcher branch from c208f87 to c8b442e Compare December 18, 2016 08:01
@ches
Copy link
Contributor Author

ches commented Dec 18, 2016

@ktonga Okay I reproduced that, thanks for the report. It looks like it's similar to the problem before where using the vim object within the Ensime constructor is problematic, because I put self._editor = Editor(self._vim) in the constructor now and internally that does an eval to eagerly check has('nvim'). Interesting that this didn't seem to be a problem for regular Vim.

I've just pushed an update that reverts that change. Maybe it would be better for avoiding surprises to make Editor.isneovim evaluate lazily and memoize instead of doing that evaluation in the constructor—I'll play with that, but for now I'm just reverting to the prior impl.

@ches ches force-pushed the dotensime-launcher branch from c8b442e to fefc881 Compare January 2, 2017 17:17
@ches ches changed the title WIP: Support launching server preinstalled by build tools Support launching server preinstalled by build tools Jan 2, 2017
@ches
Copy link
Contributor Author

ches commented Jan 2, 2017

I finally got around to adding some initial unit tests that cover at least the new stuff on this PR (updated on the branch now). As we all know the installation part of the sbt bootstrap strategy is unpleasant to unit test so I leave that matter for #312 and/or the day we remove support for it altogether…

I'd like to give EnsimeProcess some love eventually too but that's for another day and another ticket.

ches added 2 commits January 3, 2017 00:48
Types > strings. Minor aesthetic renaming, bump bootstrap sbt-coursier.
ensime-sbt now installs the server and puts all the relevant jar paths
for launching into .ensime, so we don't have to be bothered with doing
the installation. This adds support for launching this way, keeping the
bootstrap server installation around for an upgrade period.

Closes #353.
@ches ches force-pushed the dotensime-launcher branch from fefc881 to 272ba49 Compare January 2, 2017 17:57
CI infrastructure has been under maintenance so #359 didn't get checked.
@ches ches merged commit f3fd702 into ensime:master Jan 2, 2017
@ches ches deleted the dotensime-launcher branch January 2, 2017 18:11
ches referenced this pull request in ches/ensime-vim Jan 4, 2017
Since #354 it was possible that we checked for the presence of an
assembly jar in the config directory before ever creating that dir
ourselves.

This should fix #363.
ches added a commit that referenced this pull request Jan 5, 2017
Since #354 it was possible that we checked for the presence of an
assembly jar in the config directory before ever creating that dir
ourselves.

This should fix #363.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants