@@ -22,14 +22,14 @@ import com.jetbrains.ls.kotlinLsp.util.addKotlinStdlib
22
22
import com.jetbrains.ls.kotlinLsp.util.logSystemInfo
23
23
import com.jetbrains.ls.snapshot.api.impl.core.createServerStarterAnalyzerImpl
24
24
import com.jetbrains.lsp.implementation.*
25
- import kotlinx.coroutines.*
25
+ import kotlinx.coroutines.CompletableDeferred
26
+ import kotlinx.coroutines.Dispatchers
27
+ import kotlinx.coroutines.awaitCancellation
28
+ import kotlinx.coroutines.runBlocking
26
29
import org.jetbrains.kotlin.idea.base.plugin.artifacts.KotlinArtifacts
27
30
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinPluginLayoutMode
28
31
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinPluginLayoutModeProvider
29
32
import org.jetbrains.kotlin.idea.compiler.configuration.isRunningFromSources
30
- import java.io.InputStream
31
- import java.io.OutputStream
32
- import java.net.Socket
33
33
import kotlin.io.path.absolutePathString
34
34
import kotlin.io.path.createTempDirectory
35
35
@@ -45,11 +45,19 @@ private class RunKotlinLspCommand : CliktCommand(name = "kotlin-lsp") {
45
45
.help(" Whether the Kotlin LSP server is used in client mode. If not set, server mode will be used with a port specified by `${::socket.name} `" )
46
46
.validate { if (it && stdio) fail(" Can't use stdio mode with client mode" ) }
47
47
48
+ val multiclient: Boolean by option().flag()
49
+ .help(" Whether the Kotlin LSP server is used in multiclient mode. If not set, server will be shut down after the first client disconnects.`" )
50
+ .validate {
51
+ if (it && stdio) fail(" Stdio mode doesn't support multiclient mode" )
52
+ if (it && client) fail(" Client mode doesn't support multiclient mode" )
53
+ }
54
+
55
+
48
56
private fun createRunConfig (): KotlinLspServerRunConfig {
49
57
val mode = when {
50
58
stdio -> KotlinLspServerMode .Stdio
51
- client -> KotlinLspServerMode .Socket .Client (socket)
52
- else -> KotlinLspServerMode .Socket .Server (socket)
59
+ client -> KotlinLspServerMode .Socket ( TcpConnectionConfig .Client (port = socket) )
60
+ else -> KotlinLspServerMode .Socket ( TcpConnectionConfig .Server (port = socket, isMulticlient = multiclient) )
53
61
}
54
62
return KotlinLspServerRunConfig (mode)
55
63
}
@@ -76,16 +84,17 @@ private fun run(runConfig: KotlinLspServerRunConfig) {
76
84
KotlinLspServerMode .Stdio -> {
77
85
val stdout = System .out
78
86
System .setOut(System .err)
79
- handleRequests(System .`in `, stdout, config, mode)
87
+ stdioConnection(System .`in `, stdout) { connection ->
88
+ handleRequests(connection, config, mode)
89
+ }
80
90
}
81
91
82
92
is KotlinLspServerMode .Socket -> {
83
93
logSystemInfo()
84
94
tcpConnection(
85
- clientMode = mode is KotlinLspServerMode .Socket .Client ,
86
- port = mode.port,
95
+ mode.config,
87
96
) { connection ->
88
- handleRequests(connection.inputStream, connection.outputStream , config, mode)
97
+ handleRequests(connection, config, mode)
89
98
}
90
99
}
91
100
}
@@ -94,11 +103,19 @@ private fun run(runConfig: KotlinLspServerRunConfig) {
94
103
}
95
104
96
105
context(LSServerContext )
97
- private suspend fun handleRequests (input : InputStream , output : OutputStream , config : LSConfiguration , mode : KotlinLspServerMode ) {
98
- withBaseProtocolFraming(input, output) { incoming, outgoing ->
106
+ private suspend fun handleRequests (connection : LspConnection , config : LSConfiguration , mode : KotlinLspServerMode ) {
107
+ val shutdownOnExitSignal = when (mode) {
108
+ is KotlinLspServerMode .Socket -> when (val tcpConfig = mode.config) {
109
+ is TcpConnectionConfig .Client -> true
110
+ is TcpConnectionConfig .Server -> ! tcpConfig.isMulticlient
111
+ }
112
+ KotlinLspServerMode .Stdio -> true
113
+ }
114
+ val exitSignal = if (shutdownOnExitSignal) CompletableDeferred <Unit >() else null
115
+
116
+ withBaseProtocolFraming(connection, exitSignal) { incoming, outgoing ->
99
117
withServer {
100
- val exitSignal = CompletableDeferred <Unit >()
101
- val handler = createLspHandlers(config, exitSignal, clientMode = mode is KotlinLspServerMode .Socket .Client )
118
+ val handler = createLspHandlers(config, exitSignal)
102
119
103
120
withLsp(
104
121
incoming,
@@ -108,7 +125,11 @@ private suspend fun handleRequests(input: InputStream, output: OutputStream, con
108
125
Client .contextElement(lspClient)
109
126
},
110
127
) { lsp ->
111
- exitSignal.await()
128
+ if (exitSignal != null ) {
129
+ exitSignal.await()
130
+ } else {
131
+ awaitCancellation()
132
+ }
112
133
}
113
134
}
114
135
}
@@ -164,12 +185,12 @@ fun createConfiguration(
164
185
}
165
186
166
187
context(LSServer )
167
- fun createLspHandlers (config : LSConfiguration , exitSignal : CompletableDeferred <Unit >, clientMode : Boolean = false ): LspHandlers {
188
+ fun createLspHandlers (config : LSConfiguration , exitSignal : CompletableDeferred <Unit >? ): LspHandlers {
168
189
with (config) {
169
190
return lspHandlers {
170
191
initializeRequest()
171
192
setTraceNotification()
172
- shutdownRequest(clientMode, exitSignal)
193
+ shutdownRequest(exitSignal)
173
194
fileUpdateRequests()
174
195
features()
175
196
}
0 commit comments