IJPL-249975 LSP: cancel server-initiated progress when the server stops

(cherry picked from commit 48d77f51b822805d11595ce9ab7bda2263e23a55)

IJ-CR-213221

GitOrigin-RevId: 1e6eca6c163663295503f56481e4714124bee56b
This commit is contained in:
Tomasz Blachut
2026-07-20 12:53:33 +00:00
committed by intellij-monorepo-bot
parent 2bcb9629c0
commit b2e9d1a3e3
2 changed files with 17 additions and 3 deletions
+2 -2
View File
@@ -17,7 +17,6 @@ import com.intellij.platform.lsp.api.LspClientManagerListener
import com.intellij.platform.lsp.api.LspIntegrationProvider
import com.intellij.platform.lsp.api.LspCommunicationChannel
import com.intellij.platform.lsp.api.LspCommunicationChannel.StdIO
import com.intellij.platform.lsp.api.LspServerNotificationsHandler
import com.intellij.platform.lsp.api.LspServerState
import com.intellij.platform.lsp.impl.connector.Lsp4jServerConnector
import com.intellij.platform.lsp.impl.connector.Lsp4jServerConnectorSocket
@@ -87,7 +86,7 @@ class LspClientImpl internal constructor(
val requestExecutor: LspRequestExecutor = LspRequestExecutor(this, documentMapping)
internal val globMatcher: LspGlobMatcher = LspGlobMatcher()
internal val dynamicCapabilities: LspDynamicCapabilities = LspDynamicCapabilities()
internal val serverNotificationsHandler: LspServerNotificationsHandler = LspServerNotificationsHandlerImpl(this)
internal val serverNotificationsHandler: LspServerNotificationsHandlerImpl = LspServerNotificationsHandlerImpl(this)
internal val documentSyncManager = LspDocumentSyncManager(this)
internal val watchedFiles = LspWatchedFiles(this)
@@ -296,6 +295,7 @@ class LspClientImpl internal constructor(
}
documentSyncManager.clearOpenedFiles()
requestExecutor.shutdownNow()
serverNotificationsHandler.cancelAllProgress()
highlightingCacheRegistry.clearCache()
@@ -21,6 +21,7 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.platform.ide.progress.withBackgroundProgress
import com.intellij.platform.lsp.api.LspBundle
import com.intellij.platform.lsp.api.LspServerNotificationsHandler
import com.intellij.platform.lsp.api.LspServerState
import com.intellij.platform.lsp.impl.features.LspFeaturesRefreshing
import com.intellij.platform.lsp.impl.util.LspWorkspaceEditApplier
import com.intellij.platform.lsp.util.getOffsetInDocument
@@ -249,7 +250,9 @@ internal class LspServerNotificationsHandlerImpl(private val lspClient: LspClien
cancellable = value.cancellable ?: false) {
coroutineContext.job.invokeOnCompletion { throwable ->
if (throwable is CancellationException && value.cancellable == true) {
// A cancellation while the server is running means the user cancelled the indicator, so tell the server.
// A cancellation after the server stopped comes from cancelAllProgress(); there is no server to notify anymore.
if (throwable is CancellationException && value.cancellable == true && lspClient.state == LspServerState.Running) {
lspClient.sendNotification { it.cancelProgress(WorkDoneProgressCancelParams(token)) }
}
progressJobs.remove(tokenId)
@@ -284,6 +287,17 @@ internal class LspServerNotificationsHandlerImpl(private val lspClient: LspClien
}
}
/**
* Cancels every in-flight progress indicator started by the server. Called when the server stops so its background
* progresses don't keep running. The state is already set to shutdown by then, so the completion handler in
* [notifyProgress] won't send a `window/workDoneProgress/cancel` back to the server that is going away.
*/
internal fun cancelAllProgress() {
progressTasks.clear()
progressJobs.values.forEach { it.cancel() }
progressJobs.clear()
}
override fun refreshSemanticTokens(): CompletableFuture<Void> {
if (!project.isDisposed) {
lspClient.refreshSemanticTokens()