When lastDocumentDisconnectedTimeout is set to a non-trivial value (e.g., 180 seconds), the LSP console UI displays "stopping pid:12345" with an animated spinner and an elapsed-time counter for the entire duration of the grace period. The server is still fully functional during this time — it accepts new connections and processes requests — but the UI incorrectly suggests it is in the process of shutting down.
Steps to Reproduce
- Register a language server with a
lastDocumentDisconnectedTimeout > 5 seconds, e.g.:
<server id="myserver" name="MyServer"
lastDocumentDisconnectedTimeout="180"
factoryClass="..."/>
- Open a file that triggers the language server to start.
- Close the file (or close all files associated with the server).
- Observe the Language Servers tool window (LSP console tree).
Expected: The server shows as "started pid:12345" (or a new "idle" state) during the grace period, since it's still alive and accepting connections.
Actual: The server immediately shows "stopping pid:12345" with a spinning animation and an elapsed-time counter that ticks for the full 180 seconds.
Root Cause
In LanguageServerWrapper.startStopTimer() (line ~606):
private void startStopTimer() {
updateStatus(ServerStatus.stopping); // ← BUG: status set immediately
int delayMs = (int) TimeUnit.SECONDS.toMillis(
serverDefinition.getLastDocumentDisconnectedTimeout());
getStopAlarm().addRequest(() -> {
try {
stop();
} catch (Throwable t) {
LOGGER.error("...", t);
}
}, delayMs);
}
updateStatus(ServerStatus.stopping) is called immediately when the timer starts, not when the server actually begins its shutdown sequence. This triggers:
LanguageServerProcessTreeNode.setServerStatus(stopping) — sets displayName to "stopping pid:..." and starts the elapsed-time counter (startTime = System.currentTimeMillis()).
LanguageServerTreeRenderer — detects ServerStatus.stopping and renders the animated spinner icon + elapsed time text.
When a new file connects during the grace period, removeStopTimer(false) correctly cancels the alarm and restores ServerStatus.started:
private void removeStopTimer(boolean stopping) {
Alarm stopAlarm = this.stopAlarm;
if (stopAlarm != null) {
if (stopAlarm.getActiveRequestCount() > 0) {
stopAlarm.cancelAllRequests();
if (!stopping) {
updateStatus(ServerStatus.started); // ← restored here
}
}
}
}
But during the idle period without a reconnection, the server appears as "stopping" the whole time.
Impact
- With the default 5-second timeout, this is barely noticeable.
- With longer timeouts (30s, 60s, 180s — common for expensive-to-start servers like CiderLSP), the UI is misleading. Users may think the server is broken or unresponsive.
- The elapsed time counter keeps ticking, reinforcing the false impression that a slow shutdown is in progress.
- The animated spinner icon (
AnimatedIcon.Default) continuously animates, adding visual noise.
Suggested Fixes
Option A: Defer status change (minimal change)
Move updateStatus(ServerStatus.stopping) inside the alarm callback, just before stop():
private void startStopTimer() {
// Don't change status yet — the server is still alive and functional
int delayMs = (int) TimeUnit.SECONDS.toMillis(
serverDefinition.getLastDocumentDisconnectedTimeout());
getStopAlarm().addRequest(() -> {
try {
updateStatus(ServerStatus.stopping); // ← moved here
stop();
} catch (Throwable t) {
LOGGER.error("...", t);
}
}, delayMs);
}
Trade-off: During the grace period the status stays started, which is accurate (server is running) but doesn't communicate that it will stop soon.
Option B: New idle / waiting_to_stop status (more informative)
Introduce a new ServerStatus value (e.g., idle or waiting_to_stop) that the tree renderer displays differently — e.g., "idle pid:12345" with no spinner and no elapsed time counter.
This gives users accurate feedback: the server is alive but has no open documents, and will auto-stop if no files are opened soon.
Environment
- lsp4ij version: (current HEAD)
- IntelliJ IDEA version: 2025.1+
- OS: Linux
When
lastDocumentDisconnectedTimeoutis set to a non-trivial value (e.g., 180 seconds), the LSP console UI displays "stopping pid:12345" with an animated spinner and an elapsed-time counter for the entire duration of the grace period. The server is still fully functional during this time — it accepts new connections and processes requests — but the UI incorrectly suggests it is in the process of shutting down.Steps to Reproduce
lastDocumentDisconnectedTimeout> 5 seconds, e.g.:Expected: The server shows as "started pid:12345" (or a new "idle" state) during the grace period, since it's still alive and accepting connections.
Actual: The server immediately shows "stopping pid:12345" with a spinning animation and an elapsed-time counter that ticks for the full 180 seconds.
Root Cause
In
LanguageServerWrapper.startStopTimer()(line ~606):updateStatus(ServerStatus.stopping)is called immediately when the timer starts, not when the server actually begins its shutdown sequence. This triggers:LanguageServerProcessTreeNode.setServerStatus(stopping)— setsdisplayNameto"stopping pid:..."and starts the elapsed-time counter (startTime = System.currentTimeMillis()).LanguageServerTreeRenderer— detectsServerStatus.stoppingand renders the animated spinner icon + elapsed time text.When a new file connects during the grace period,
removeStopTimer(false)correctly cancels the alarm and restoresServerStatus.started:But during the idle period without a reconnection, the server appears as "stopping" the whole time.
Impact
AnimatedIcon.Default) continuously animates, adding visual noise.Suggested Fixes
Option A: Defer status change (minimal change)
Move
updateStatus(ServerStatus.stopping)inside the alarm callback, just beforestop():Trade-off: During the grace period the status stays
started, which is accurate (server is running) but doesn't communicate that it will stop soon.Option B: New
idle/waiting_to_stopstatus (more informative)Introduce a new
ServerStatusvalue (e.g.,idleorwaiting_to_stop) that the tree renderer displays differently — e.g., "idle pid:12345" with no spinner and no elapsed time counter.This gives users accurate feedback: the server is alive but has no open documents, and will auto-stop if no files are opened soon.
Environment