-
Notifications
You must be signed in to change notification settings - Fork 54
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
fix: propagate LanguagServerWrapper startup exception correctly #1044
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -242,8 +242,11 @@ private List<WorkspaceFolder> getRelevantWorkspaceFolders() { | |
} | ||
|
||
/** | ||
* Starts a language server and triggers initialization. If language server is | ||
* started and active, does nothing. If language server is inactive, restart it. | ||
* Starts a language server and triggers initialization. If language server has been started | ||
* before, does nothing. | ||
* If the initialization throws an exception (e.g. because the binary for the LS could not be found), | ||
* call {@link #restart()} to force another startup attempt (e.g. because the exception could be handled programmatically). | ||
* Use {@link #startupFailed()} to check if an exception has occurred. | ||
*/ | ||
public synchronized void start() { | ||
start(false); | ||
|
@@ -279,7 +282,7 @@ private synchronized void start(boolean forceRestart) { | |
stop(); | ||
} | ||
} | ||
if (this.initializeFuture == null) { | ||
if (this.initializeFuture == null || forceRestart) { | ||
final URI rootURI = getRootURI(); | ||
final Job job = createInitializeLanguageServerJob(); | ||
this.launcherFuture = new CompletableFuture<>(); | ||
|
@@ -354,7 +357,7 @@ private synchronized void start(boolean forceRestart) { | |
FileBuffers.getTextFileBufferManager().addFileBufferListener(fileBufferListener); | ||
advanceInitializeFutureMonitor(); | ||
}).exceptionally(e -> { | ||
stop(); | ||
shutdown(); | ||
final Throwable cause = e.getCause(); | ||
if (cause instanceof CancellationException c) { | ||
throw c; | ||
|
@@ -486,6 +489,13 @@ public synchronized boolean isActive() { | |
return launcherFuture != null && !launcherFuture.isDone(); | ||
} | ||
|
||
/** | ||
* @return whether the last startup attempt has failed | ||
*/ | ||
public synchronized boolean startupFailed() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this method called by anyone? What's the purpose of this method? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is to determine whether a startup attempt on this LanguageServerWrapper object failed and a call to restart() is needed. We will be using it in our project. |
||
return this.initializeFuture != null && this.initializeFuture.isCompletedExceptionally(); | ||
} | ||
|
||
private void removeStopTimerTask() { | ||
synchronized (timer) { | ||
if (stopTimerTask != null) { | ||
|
@@ -521,6 +531,14 @@ boolean isWrapperFor(LanguageServer server) { | |
} | ||
|
||
public synchronized void stop() { | ||
if (this.initializeFuture != null) { | ||
initializeFuture.cancel(true); | ||
this.initializeFuture= null; | ||
} | ||
shutdown(); | ||
} | ||
|
||
private void shutdown() { | ||
final boolean alreadyStopping = this.stopping.getAndSet(true); | ||
if (alreadyStopping) { | ||
return; | ||
|
@@ -531,11 +549,6 @@ public synchronized void stop() { | |
this.languageClient.dispose(); | ||
} | ||
|
||
if (this.initializeFuture != null) { | ||
this.initializeFuture.cancel(true); | ||
this.initializeFuture = null; | ||
} | ||
|
||
this.serverCapabilities = null; | ||
this.dynamicRegistrations.clear(); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If an exception has been occurred during start. The LS cannot be started again when the cause has been fixed (e.g. wrong path do LS binary).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it could we worth explaining as well that "an Eclipse-base product can check
startupFailed
and call restart if the problem has been programmatically handled", and that otherwise "once the problem is fixed a restart of eclipse is needed". That would clarify as well the need for thestartupFailed
method for future readers of this class. My wording can most probably be improved :)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
..but I am okay with it. This can/should be handled by the LS provider