Skip to content

Commit e11a46c

Browse files
committed
feat(q): stub out Q LSP logic
1 parent 9e3760e commit e11a46c

File tree

6 files changed

+230
-0
lines changed

6 files changed

+230
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package software.aws.toolkits.jetbrains.services.amazonq.lsp
2+
3+
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest
4+
import org.eclipse.lsp4j.services.LanguageClient
5+
import java.util.concurrent.CompletableFuture
6+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.ConnectionMetadata
7+
8+
interface AmazonQLanguageClient : LanguageClient {
9+
@JsonRequest("aws/credentials/getConnectionMetadata")
10+
fun getConnectionMetadata(): CompletableFuture<ConnectionMetadata>
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.amazonq.lsp
5+
6+
import com.intellij.notification.NotificationType
7+
import org.eclipse.lsp4j.ConfigurationParams
8+
import org.eclipse.lsp4j.MessageActionItem
9+
import org.eclipse.lsp4j.MessageParams
10+
import org.eclipse.lsp4j.MessageType
11+
import org.eclipse.lsp4j.PublishDiagnosticsParams
12+
import org.eclipse.lsp4j.ShowMessageRequestParams
13+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.ConnectionMetadata
14+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.SsoProfileData
15+
import java.util.concurrent.CompletableFuture
16+
17+
class AmazonQLanguageClientImpl : AmazonQLanguageClient {
18+
override fun telemetryEvent(`object`: Any) {
19+
println(`object`)
20+
}
21+
22+
override fun publishDiagnostics(diagnostics: PublishDiagnosticsParams) {
23+
println(diagnostics)
24+
}
25+
26+
override fun showMessage(messageParams: MessageParams) {
27+
val type = when (messageParams.type) {
28+
MessageType.Error -> NotificationType.ERROR
29+
MessageType.Warning -> NotificationType.WARNING
30+
MessageType.Info, MessageType.Log -> NotificationType.INFORMATION
31+
}
32+
println("$type: ${messageParams.message}")
33+
}
34+
35+
override fun showMessageRequest(requestParams: ShowMessageRequestParams): CompletableFuture<MessageActionItem?>? {
36+
println(requestParams)
37+
38+
return CompletableFuture.completedFuture(null)
39+
}
40+
41+
override fun logMessage(message: MessageParams) {
42+
showMessage(message)
43+
}
44+
45+
override fun getConnectionMetadata() = CompletableFuture.completedFuture(
46+
ConnectionMetadata(
47+
SsoProfileData("TODO")
48+
)
49+
)
50+
51+
override fun configuration(params: ConfigurationParams): CompletableFuture<List<Any>> {
52+
if (params.items.isEmpty()) {
53+
return CompletableFuture.completedFuture(null)
54+
}
55+
56+
return CompletableFuture.completedFuture(
57+
buildList {
58+
}
59+
)
60+
}
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package software.aws.toolkits.jetbrains.services.amazonq.lsp
2+
3+
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage
4+
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest
5+
import org.eclipse.lsp4j.services.LanguageServer
6+
import java.util.concurrent.CompletableFuture
7+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.UpdateCredentialsPayload
8+
9+
interface AmazonQLanguageServer : LanguageServer {
10+
@JsonRequest("aws/credentials/token/update")
11+
fun updateTokenCredentials(payload: UpdateCredentialsPayload): CompletableFuture<ResponseMessage>
12+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.amazonq.lsp
5+
6+
import com.google.gson.ToNumberPolicy
7+
import com.intellij.execution.configurations.GeneralCommandLine
8+
import com.intellij.execution.impl.ExecutionManagerImpl
9+
import com.intellij.execution.process.KillableColoredProcessHandler
10+
import com.intellij.execution.process.ProcessEvent
11+
import com.intellij.execution.process.ProcessListener
12+
import com.intellij.execution.process.ProcessOutputType
13+
import com.intellij.openapi.Disposable
14+
import com.intellij.openapi.components.Service
15+
import com.intellij.openapi.components.service
16+
import com.intellij.openapi.project.Project
17+
import com.intellij.openapi.util.Key
18+
import org.eclipse.lsp4j.jsonrpc.Launcher
19+
import org.eclipse.lsp4j.launch.LSPLauncher
20+
import software.aws.toolkits.core.utils.getLogger
21+
import software.aws.toolkits.jetbrains.isDeveloperMode
22+
import java.io.IOException
23+
import java.io.OutputStreamWriter
24+
import java.io.PipedInputStream
25+
import java.io.PipedOutputStream
26+
import java.io.PrintWriter
27+
import java.io.StringWriter
28+
import java.nio.charset.StandardCharsets
29+
import org.slf4j.event.Level
30+
31+
// https://github.com/redhat-developer/lsp4ij/blob/main/src/main/java/com/redhat/devtools/lsp4ij/server/LSPProcessListener.java
32+
// JB impl and redhat both use a wrapper to handle input buffering issue
33+
internal class LSPProcessListener : ProcessListener {
34+
private val outputStream = PipedOutputStream()
35+
private val outputStreamWriter = OutputStreamWriter(outputStream, StandardCharsets.UTF_8)
36+
val inputStream = PipedInputStream(outputStream)
37+
38+
override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) {
39+
if (ProcessOutputType.isStdout(outputType)) {
40+
try {
41+
this.outputStreamWriter.write(event.text)
42+
this.outputStreamWriter.flush()
43+
} catch (_: IOException) {
44+
ExecutionManagerImpl.stopProcess(event.processHandler)
45+
}
46+
} else if (ProcessOutputType.isStderr(outputType)) {
47+
LOG.warn("LSP process stderr: ${event.text}")
48+
}
49+
}
50+
51+
override fun processTerminated(event: ProcessEvent) {
52+
try {
53+
this.outputStreamWriter.close()
54+
this.outputStream.close()
55+
} catch (_: IOException) {
56+
}
57+
}
58+
59+
companion object {
60+
private val LOG = getLogger<LSPProcessListener>()
61+
}
62+
}
63+
64+
@Service(Service.Level.PROJECT)
65+
class AmazonQLspService(project: Project) : Disposable {
66+
private val launcher: Launcher<AmazonQLanguageServer>
67+
68+
private val languageServer: AmazonQLanguageServer
69+
get() = launcher.remoteProxy
70+
71+
init {
72+
val cmd = GeneralCommandLine("amazon-q-lsp")
73+
74+
val handler = KillableColoredProcessHandler.Silent(cmd)
75+
val inputWrapper = LSPProcessListener()
76+
handler.addProcessListener(inputWrapper)
77+
78+
handler.startNotify()
79+
80+
launcher = LSPLauncher.Builder<AmazonQLanguageServer>()
81+
.setLocalService(AmazonQLanguageClientImpl())
82+
.setRemoteInterface(AmazonQLanguageServer::class.java)
83+
.configureGson {
84+
// TODO: maybe need adapter for initialize:
85+
// https://github.com/aws/amazon-q-eclipse/blob/b9d5bdcd5c38e1dd8ad371d37ab93a16113d7d4b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/QLspTypeAdapterFactory.java
86+
87+
// otherwise Gson treats all numbers as double which causes deser issues
88+
it.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
89+
}.traceMessages(
90+
PrintWriter(
91+
object : StringWriter() {
92+
private val traceLogger = LOG.atLevel(if (isDeveloperMode()) Level.INFO else Level.DEBUG)
93+
94+
override fun flush() {
95+
traceLogger.log(buffer.toString())
96+
buffer.setLength(0)
97+
}
98+
}
99+
)
100+
)
101+
.setInput(inputWrapper.inputStream)
102+
.setOutput(handler.process.outputStream)
103+
.create()
104+
105+
launcher.startListening()
106+
}
107+
108+
override fun dispose() {
109+
languageServer.apply {
110+
shutdown().thenRun { exit() }
111+
}
112+
}
113+
114+
companion object {
115+
private val LOG = getLogger<AmazonQLspService>()
116+
fun getInstance(project: Project): AmazonQLspService {
117+
return project.service()
118+
}
119+
}
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials
5+
6+
data class ConnectionMetadata(
7+
val sso: SsoProfileData
8+
)
9+
10+
data class SsoProfileData(
11+
val startUrl: String
12+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials
2+
3+
data class UpdateCredentialsPayload(
4+
val data: String,
5+
val encrypted: String,
6+
)
7+
8+
data class UpdateCredentialsPayloadData(
9+
val data: BearerCredentials
10+
)
11+
12+
data class BearerCredentials(
13+
val token: String
14+
)

0 commit comments

Comments
 (0)