-
Notifications
You must be signed in to change notification settings - Fork 275
feat(q): stub out Q LSP logic #5352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package software.aws.toolkits.jetbrains.services.amazonq.lsp | ||
|
|
||
| import org.eclipse.lsp4j.jsonrpc.services.JsonRequest | ||
| import org.eclipse.lsp4j.services.LanguageClient | ||
| import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.ConnectionMetadata | ||
| import java.util.concurrent.CompletableFuture | ||
|
|
||
| /** | ||
| * Requests sent by server to client | ||
| */ | ||
| @Suppress("unused") | ||
| interface AmazonQLanguageClient : LanguageClient { | ||
| @JsonRequest("aws/credentials/getConnectionMetadata") | ||
| fun getConnectionMetadata(): CompletableFuture<ConnectionMetadata> | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| // Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package software.aws.toolkits.jetbrains.services.amazonq.lsp | ||
|
|
||
| import com.intellij.notification.NotificationType | ||
| import org.eclipse.lsp4j.ConfigurationParams | ||
| import org.eclipse.lsp4j.MessageActionItem | ||
| import org.eclipse.lsp4j.MessageParams | ||
| import org.eclipse.lsp4j.MessageType | ||
| import org.eclipse.lsp4j.PublishDiagnosticsParams | ||
| import org.eclipse.lsp4j.ShowMessageRequestParams | ||
| import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.ConnectionMetadata | ||
| import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.SsoProfileData | ||
| import java.util.concurrent.CompletableFuture | ||
|
|
||
| /** | ||
| * Concrete implementation of [AmazonQLanguageClient] to handle messages sent from server | ||
| */ | ||
| class AmazonQLanguageClientImpl : AmazonQLanguageClient { | ||
|
Check warning on line 20 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt
|
||
| override fun telemetryEvent(`object`: Any) { | ||
| println(`object`) | ||
| } | ||
|
Check warning on line 23 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt
|
||
|
|
||
| override fun publishDiagnostics(diagnostics: PublishDiagnosticsParams) { | ||
| println(diagnostics) | ||
rli marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
Check warning on line 27 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt
|
||
|
|
||
| override fun showMessage(messageParams: MessageParams) { | ||
| val type = when (messageParams.type) { | ||
| MessageType.Error -> NotificationType.ERROR | ||
| MessageType.Warning -> NotificationType.WARNING | ||
| MessageType.Info, MessageType.Log -> NotificationType.INFORMATION | ||
|
Check warning on line 33 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt
|
||
| } | ||
| println("$type: ${messageParams.message}") | ||
rli marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
Check warning on line 36 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt
|
||
|
|
||
| override fun showMessageRequest(requestParams: ShowMessageRequestParams): CompletableFuture<MessageActionItem?>? { | ||
| println(requestParams) | ||
|
Check warning on line 39 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt
|
||
|
|
||
| return CompletableFuture.completedFuture(null) | ||
|
Check warning on line 41 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt
|
||
| } | ||
|
|
||
| override fun logMessage(message: MessageParams) { | ||
| showMessage(message) | ||
| } | ||
|
Check warning on line 46 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt
|
||
|
|
||
| override fun getConnectionMetadata() = CompletableFuture.completedFuture( | ||
|
Check notice on line 48 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt
|
||
Check noticeCode scanning / QDJVMC Function or property has platform type Note
Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. Specify type explicitly as nullable or non-nullable.
|
||
| ConnectionMetadata( | ||
| SsoProfileData("TODO") | ||
|
Check warning on line 50 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt
|
||
| ) | ||
| ) | ||
|
Check warning on line 52 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt
|
||
|
|
||
| override fun configuration(params: ConfigurationParams): CompletableFuture<List<Any>> { | ||
| if (params.items.isEmpty()) { | ||
| return CompletableFuture.completedFuture(null) | ||
|
Check warning on line 56 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt
|
||
| } | ||
|
|
||
| return CompletableFuture.completedFuture( | ||
| buildList { | ||
| } | ||
|
Check warning on line 61 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt
|
||
| ) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package software.aws.toolkits.jetbrains.services.amazonq.lsp | ||
|
|
||
| import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage | ||
| import org.eclipse.lsp4j.jsonrpc.services.JsonRequest | ||
| import org.eclipse.lsp4j.services.LanguageServer | ||
| import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.UpdateCredentialsPayload | ||
| import java.util.concurrent.CompletableFuture | ||
|
|
||
| /** | ||
| * Remote interface exposed by the Amazon Q language server | ||
| */ | ||
| @Suppress("unused") | ||
| interface AmazonQLanguageServer : LanguageServer { | ||
| @JsonRequest("aws/credentials/token/update") | ||
| fun updateTokenCredentials(payload: UpdateCredentialsPayload): CompletableFuture<ResponseMessage> | ||
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| // Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package software.aws.toolkits.jetbrains.services.amazonq.lsp | ||
|
|
||
| import com.google.gson.ToNumberPolicy | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we be using gson?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes lsp4j only works with gson |
||
| import com.intellij.execution.configurations.GeneralCommandLine | ||
| import com.intellij.execution.impl.ExecutionManagerImpl | ||
| import com.intellij.execution.process.KillableColoredProcessHandler | ||
| import com.intellij.execution.process.KillableProcessHandler | ||
| import com.intellij.execution.process.ProcessEvent | ||
| import com.intellij.execution.process.ProcessListener | ||
| import com.intellij.execution.process.ProcessOutputType | ||
| import com.intellij.openapi.Disposable | ||
| import com.intellij.openapi.components.Service | ||
| import com.intellij.openapi.components.service | ||
| import com.intellij.openapi.project.Project | ||
| import com.intellij.openapi.util.Key | ||
| import com.intellij.util.io.await | ||
| import kotlinx.coroutines.CoroutineScope | ||
| import kotlinx.coroutines.launch | ||
| import org.eclipse.lsp4j.InitializeParams | ||
| import org.eclipse.lsp4j.InitializedParams | ||
| import org.eclipse.lsp4j.jsonrpc.Launcher | ||
| import org.eclipse.lsp4j.launch.LSPLauncher | ||
| import org.slf4j.event.Level | ||
| import software.aws.toolkits.core.utils.getLogger | ||
| import software.aws.toolkits.core.utils.warn | ||
| import software.aws.toolkits.jetbrains.isDeveloperMode | ||
| import java.io.IOException | ||
| import java.io.OutputStreamWriter | ||
| import java.io.PipedInputStream | ||
| import java.io.PipedOutputStream | ||
| import java.io.PrintWriter | ||
| import java.io.StringWriter | ||
| import java.nio.charset.StandardCharsets | ||
| import java.util.concurrent.Future | ||
|
|
||
| // https://github.com/redhat-developer/lsp4ij/blob/main/src/main/java/com/redhat/devtools/lsp4ij/server/LSPProcessListener.java | ||
| // JB impl and redhat both use a wrapper to handle input buffering issue | ||
| internal class LSPProcessListener : ProcessListener { | ||
| private val outputStream = PipedOutputStream() | ||
| private val outputStreamWriter = OutputStreamWriter(outputStream, StandardCharsets.UTF_8) | ||
| val inputStream = PipedInputStream(outputStream) | ||
|
Check warning on line 44 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
|
|
||
| override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { | ||
| if (ProcessOutputType.isStdout(outputType)) { | ||
| try { | ||
| this.outputStreamWriter.write(event.text) | ||
| this.outputStreamWriter.flush() | ||
| } catch (_: IOException) { | ||
| ExecutionManagerImpl.stopProcess(event.processHandler) | ||
|
Check warning on line 52 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| } | ||
| } else if (ProcessOutputType.isStderr(outputType)) { | ||
| LOG.warn { "LSP process stderr: ${event.text}" } | ||
|
Check warning on line 55 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| } | ||
| } | ||
|
Check warning on line 57 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
|
|
||
| override fun processTerminated(event: ProcessEvent) { | ||
| try { | ||
| this.outputStreamWriter.close() | ||
| this.outputStream.close() | ||
| } catch (_: IOException) { | ||
|
Check warning on line 63 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| } | ||
| } | ||
|
Check warning on line 65 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
|
|
||
| companion object { | ||
| private val LOG = getLogger<LSPProcessListener>() | ||
|
Check warning on line 68 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| } | ||
| } | ||
|
|
||
| @Service(Service.Level.PROJECT) | ||
| class AmazonQLspService(project: Project, private val cs: CoroutineScope) : Disposable { | ||
|
Check warning on line 73 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
Check warningCode scanning / QDJVMC Unused symbol Warning
Class "AmazonQLspService" is never used
Check warningCode scanning / QDJVMC Constructor parameter is never used as a property Warning
Constructor parameter is never used as a property
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would this be triggered?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. future pr |
||
| private val launcher: Launcher<AmazonQLanguageServer> | ||
|
|
||
| private val languageServer: AmazonQLanguageServer | ||
| get() = launcher.remoteProxy | ||
|
Check warning on line 77 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
|
|
||
| @Suppress("ForbiddenVoid") | ||
| private val launcherFuture: Future<Void> | ||
| private val launcherHandler: KillableProcessHandler | ||
|
|
||
| init { | ||
| val cmd = GeneralCommandLine("amazon-q-lsp") | ||
|
Check warning on line 84 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
|
|
||
| launcherHandler = KillableColoredProcessHandler.Silent(cmd) | ||
| val inputWrapper = LSPProcessListener() | ||
| launcherHandler.addProcessListener(inputWrapper) | ||
| launcherHandler.startNotify() | ||
|
Check warning on line 89 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
|
|
||
| launcher = LSPLauncher.Builder<AmazonQLanguageServer>() | ||
| .setLocalService(AmazonQLanguageClientImpl()) | ||
| .setRemoteInterface(AmazonQLanguageServer::class.java) | ||
| .configureGson { | ||
|
Check warning on line 94 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| // TODO: maybe need adapter for initialize: | ||
| // https://github.com/aws/amazon-q-eclipse/blob/b9d5bdcd5c38e1dd8ad371d37ab93a16113d7d4b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/QLspTypeAdapterFactory.java | ||
|
|
||
| // otherwise Gson treats all numbers as double which causes deser issues | ||
| it.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | ||
| }.traceMessages( | ||
| PrintWriter( | ||
| object : StringWriter() { | ||
|
Check warning on line 102 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| private val traceLogger = LOG.atLevel(if (isDeveloperMode()) Level.INFO else Level.DEBUG) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ohh this is a good idea, maybe we can convert this to a util? |
||
|
|
||
| override fun flush() { | ||
| traceLogger.log { buffer.toString() } | ||
| buffer.setLength(0) | ||
| } | ||
|
Check warning on line 108 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| } | ||
| ) | ||
| ) | ||
| .setInput(inputWrapper.inputStream) | ||
| .setOutput(launcherHandler.process.outputStream) | ||
| .create() | ||
|
Check warning on line 114 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
|
|
||
| launcherFuture = launcher.startListening() | ||
|
Check warning on line 116 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
|
|
||
| cs.launch { | ||
| val initializeResult = languageServer.initialize( | ||
| InitializeParams().apply { | ||
|
Check warning on line 120 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| // does this work on windows | ||
| processId = ProcessHandle.current().pid().toInt() | ||
|
Check warning on line 122 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| // capabilities | ||
| // client info | ||
| // trace? | ||
| // workspace folders? | ||
| // anything else we need? | ||
| } | ||
|
Check warning on line 128 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| // probably need a timeout | ||
| ).await() | ||
|
Check warning on line 130 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
|
|
||
| // then if this succeeds then we can allow the client to send requests | ||
| if (initializeResult == null) { | ||
| LOG.warn { "LSP initialization failed" } | ||
| launcherHandler.destroyProcess() | ||
|
Check warning on line 135 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| } | ||
| languageServer.initialized(InitializedParams()) | ||
| } | ||
| } | ||
|
Check warning on line 139 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
|
|
||
| override fun dispose() { | ||
| if (!launcherFuture.isDone) { | ||
| try { | ||
| languageServer.apply { | ||
| shutdown().thenRun { exit() } | ||
| } | ||
| } catch (e: Exception) { | ||
| LOG.warn(e) { "LSP shutdown failed" } | ||
| launcherHandler.destroyProcess() | ||
|
Check warning on line 149 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| } | ||
| } else if (!launcherHandler.isProcessTerminated) { | ||
| launcherHandler.destroyProcess() | ||
|
Check warning on line 152 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| } | ||
| } | ||
|
Check warning on line 154 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
|
|
||
| companion object { | ||
| private val LOG = getLogger<AmazonQLspService>() | ||
| fun getInstance(project: Project) = project.service<AmazonQLspService>() | ||
|
Check warning on line 158 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| // Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials | ||
|
|
||
| data class ConnectionMetadata( | ||
| val sso: SsoProfileData, | ||
|
Check warning on line 7 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/credentials/ConnectionMetadata.kt
|
||
| ) | ||
|
|
||
| data class SsoProfileData( | ||
| val startUrl: String, | ||
|
Check warning on line 11 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/credentials/ConnectionMetadata.kt
|
||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| // Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials | ||
|
|
||
| data class UpdateCredentialsPayload( | ||
| val data: String, | ||
| val encrypted: String, | ||
|
Check warning on line 8 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/credentials/UpdateCredentialsPayload.kt
|
||
| ) | ||
|
|
||
| data class UpdateCredentialsPayloadData( | ||
|
Check warning on line 11 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/credentials/UpdateCredentialsPayload.kt
|
||
|
||
| val data: BearerCredentials, | ||
|
Check warning on line 12 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/credentials/UpdateCredentialsPayload.kt
|
||
| ) | ||
|
|
||
| data class BearerCredentials( | ||
| val token: String, | ||
|
Check warning on line 16 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/credentials/UpdateCredentialsPayload.kt
|
||
| ) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: not needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we're going to keep the prints until they are implemented properly