@@ -24,12 +24,16 @@ import com.intellij.util.net.HttpConfigurable
2424import com.intellij.util.net.JdkProxyProvider
2525import kotlinx.coroutines.CoroutineScope
2626import kotlinx.coroutines.Deferred
27+ import kotlinx.coroutines.Job
2728import kotlinx.coroutines.async
2829import kotlinx.coroutines.channels.BufferOverflow
30+ import kotlinx.coroutines.delay
2931import kotlinx.coroutines.flow.MutableSharedFlow
3032import kotlinx.coroutines.flow.asSharedFlow
3133import kotlinx.coroutines.flow.map
3234import kotlinx.coroutines.future.asCompletableFuture
35+ import kotlinx.coroutines.isActive
36+ import kotlinx.coroutines.launch
3337import kotlinx.coroutines.runBlocking
3438import kotlinx.coroutines.sync.Mutex
3539import kotlinx.coroutines.sync.withLock
@@ -50,6 +54,7 @@ import org.eclipse.lsp4j.jsonrpc.MessageConsumer
5054import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage
5155import org.eclipse.lsp4j.launch.LSPLauncher
5256import org.slf4j.event.Level
57+ import software.aws.toolkits.core.utils.debug
5358import software.aws.toolkits.core.utils.getLogger
5459import software.aws.toolkits.core.utils.info
5560import software.aws.toolkits.core.utils.warn
@@ -125,6 +130,7 @@ class AmazonQLspService(private val project: Project, private val cs: CoroutineS
125130 get() = instance.getCompleted().initializeResult.getCompleted().capabilities
126131 val encryptionManager
127132 get() = instance.getCompleted().encryptionManager
133+ private val heartbeatJob: Job
128134
129135 // dont allow lsp commands if server is restarting
130136 private val mutex = Mutex (false )
@@ -157,9 +163,36 @@ class AmazonQLspService(private val project: Project, private val cs: CoroutineS
157163
158164 init {
159165 instance = start()
166+
167+ // Initialize heartbeat job
168+ heartbeatJob = cs.launch {
169+ while (isActive) {
170+ delay(5 .seconds) // Check every 2 seconds
171+ checkConnectionStatus()
172+ }
173+ }
174+ }
175+
176+ private suspend fun checkConnectionStatus () {
177+ try {
178+ val currentInstance = mutex.withLock { instance }.await()
179+
180+ // Check if the launcher's Future (startListening) is done
181+ // If it's done, that means the connection has been terminated
182+ if (currentInstance.launcherFuture.isDone) {
183+ LOG .debug { " LSP server connection terminated, restarting server" }
184+ restart()
185+ } else {
186+ LOG .debug { " LSP server is currently running " }
187+ }
188+ } catch (e: Exception ) {
189+ LOG .debug(e) { " Connection status check failed, restarting LSP server" }
190+ restart()
191+ }
160192 }
161193
162194 override fun dispose () {
195+ heartbeatJob.cancel()
163196 }
164197
165198 suspend fun restart () = mutex.withLock {
@@ -225,7 +258,8 @@ private class AmazonQServerInstance(private val project: Project, private val cs
225258 get() = launcher.remoteProxy
226259
227260 @Suppress(" ForbiddenVoid" )
228- private val launcherFuture: Future <Void >
261+ var launcherFuture: Future <Void >
262+ private set
229263 private val launcherHandler: KillableProcessHandler
230264 val initializeResult: Deferred <InitializeResult >
231265
0 commit comments