Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9d71b4c
syncModuleDependencies call barebones
samgst-amazon Feb 26, 2025
1f1c2ac
Merge branch 'feature/q-lsp' into samgst/q-lsp-module-dependencies
samgst-amazon Feb 26, 2025
f92ab4d
Merge branch 'feature/q-lsp' into samgst/q-lsp-module-dependencies
samgst-amazon Feb 26, 2025
58e3ea2
Merge branch 'feature/q-lsp' into samgst/q-lsp-module-dependencies
samgst-amazon Feb 26, 2025
060f64a
Merge branch 'feature/q-lsp' into samgst/q-lsp-module-dependencies
samgst-amazon Mar 3, 2025
63eb25c
Merge branch 'feature/q-lsp' into samgst/q-lsp-module-dependencies
samgst-amazon Mar 3, 2025
f5f4c87
Merge branch 'feature/q-lsp' into samgst/q-lsp-module-dependencies
samgst-amazon Mar 4, 2025
10ddb2d
implement syncModuleDependencies logic
samgst-amazon Feb 27, 2025
44c794e
detekt
samgst-amazon Mar 4, 2025
90d5365
Merge branch 'refs/heads/feature/q-lsp' into samgst/q-lsp-module-depe…
samgst-amazon Mar 5, 2025
9edbfac
return completableFuture
samgst-amazon Mar 5, 2025
b9ba38f
Merge branch 'feature/q-lsp' into samgst/q-lsp-module-dependencies
samgst-amazon Mar 5, 2025
a461559
Merge branch 'feature/q-lsp' into samgst/q-lsp-module-dependencies
samgst-amazon Mar 5, 2025
5ff55ca
ExtensionPoint impl
samgst-amazon Mar 5, 2025
b2d5cc3
detekt
samgst-amazon Mar 5, 2025
a1c08a5
fix EachExtensionSafe
samgst-amazon Mar 5, 2025
f254b7a
update ExtensionPoint namespace
samgst-amazon Mar 6, 2025
c9d7ab3
private functions not needed
samgst-amazon Mar 6, 2025
7b77a6f
detekt
samgst-amazon Mar 6, 2025
3263ac9
class visibility
samgst-amazon Mar 6, 2025
7e74c9e
cs.launch for the ModuleDependencyServiceHandler
samgst-amazon Mar 6, 2025
e35e956
Tests
samgst-amazon Mar 7, 2025
d14bfe4
Merge branch 'feature/q-lsp' into samgst/q-lsp-module-dependencies
samgst-amazon Mar 7, 2025
2edaba4
update xml
samgst-amazon Mar 7, 2025
6d9eb85
move xml impl to src root
samgst-amazon Mar 7, 2025
db03df1
feedback
samgst-amazon Mar 7, 2025
2fd9ee6
change message to notification instead of Request
samgst-amazon Mar 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,17 @@
<xi:include href="/META-INF/plugin-codetransform.xml" />
<xi:include href="/META-INF/plugin-codewhisperer.xml" />

<extensionPoints>
<extensionPoint qualifiedName="software.aws.toolkits.jetbrains.moduleDependencyProvider"
interface="software.aws.toolkits.jetbrains.services.amazonq.lsp.dependencies.ModuleDependencyProvider"
dynamic="true"/>
</extensionPoints>

<extensions defaultExtensionNs="com.intellij">
<applicationService serviceImplementation="migration.software.aws.toolkits.jetbrains.services.codewhisperer.explorer.CodeWhispererExplorerActionManager"/>
</extensions>

<depends optional="true" config-file="amazon-ext-java.xml">com.intellij.modules.java</depends>
<depends optional="true" config-file="amazon-ext-python.xml">com.intellij.modules.python</depends>

</idea-plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ import org.eclipse.lsp4j.jsonrpc.services.JsonNotification
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 software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.dependencies.SyncModuleDependenciesParams
import java.util.concurrent.CompletableFuture

/**
* Remote interface exposed by the Amazon Q language server
*/
@Suppress("unused")
interface AmazonQLanguageServer : LanguageServer {
@JsonRequest("aws/syncModuleDependencies")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you see if @JsonNotification works

fun syncModuleDependencies(params: SyncModuleDependenciesParams): CompletableFuture<Unit>

@JsonRequest("aws/credentials/token/update")
fun updateTokenCredentials(payload: UpdateCredentialsPayload): CompletableFuture<ResponseMessage>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
Expand All @@ -45,6 +46,7 @@ import software.aws.toolkits.core.utils.info
import software.aws.toolkits.core.utils.warn
import software.aws.toolkits.jetbrains.isDeveloperMode
import software.aws.toolkits.jetbrains.services.amazonq.lsp.auth.DefaultAuthCredentialsService
import software.aws.toolkits.jetbrains.services.amazonq.lsp.dependencies.DefaultModuleDependenciesService
import software.aws.toolkits.jetbrains.services.amazonq.lsp.encryption.JwtEncryptionManager
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.createExtendedClientMetadata
import software.aws.toolkits.jetbrains.services.amazonq.lsp.textdocument.TextDocumentServiceHandler
Expand Down Expand Up @@ -310,6 +312,9 @@ private class AmazonQServerInstance(private val project: Project, private val cs
DefaultAuthCredentialsService(project, encryptionManager, this)
TextDocumentServiceHandler(project, this)
WorkspaceServiceHandler(project, this)
cs.launch {
DefaultModuleDependenciesService(project, this@AmazonQServerInstance)
}
}

override fun dispose() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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.dependencies

import com.intellij.openapi.Disposable
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModuleRootEvent
import com.intellij.openapi.roots.ModuleRootListener
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
import software.aws.toolkits.jetbrains.services.amazonq.lsp.dependencies.ModuleDependencyProvider.Companion.EP_NAME
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.dependencies.SyncModuleDependenciesParams
import java.util.concurrent.CompletableFuture

class DefaultModuleDependenciesService(
private val project: Project,
serverInstance: Disposable,
) : ModuleDependenciesService,
ModuleRootListener {

init {
project.messageBus.connect(serverInstance).subscribe(
ModuleRootListener.TOPIC,
this
)
// project initiation with initial list of dependencies
syncAllModules()
}

override fun rootsChanged(event: ModuleRootEvent) {
if (event.isCausedByFileTypesChange) return
// call on change with updated dependencies
syncAllModules()
}

override fun syncModuleDependencies(params: SyncModuleDependenciesParams): CompletableFuture<Unit> =
CompletableFuture<Unit>().also { completableFuture ->
AmazonQLspService.executeIfRunning(project) { languageServer ->
languageServer.syncModuleDependencies(params)
completableFuture.complete(null)
} ?: completableFuture.completeExceptionally(IllegalStateException("LSP Server not running"))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simplifies to

Suggested change
CompletableFuture<Unit>().also { completableFuture ->
AmazonQLspService.executeIfRunning(project) { languageServer ->
languageServer.syncModuleDependencies(params)
completableFuture.complete(null)
} ?: completableFuture.completeExceptionally(IllegalStateException("LSP Server not running"))
}
AmazonQLspService.executeIfRunning(project) { languageServer ->
languageServer.syncModuleDependencies(params)
}?.toCompletableFuture() ?: CompletableFuture.failedFuture(IllegalStateException("LSP Server not running"))


private fun syncAllModules() {
ModuleManager.getInstance(project).modules.forEach { module ->
EP_NAME.forEachExtensionSafe {
if (it.isApplicable(module)) {
syncModuleDependencies(it.createParams(module))
return@forEachExtensionSafe
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// 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.dependencies

import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.dependencies.SyncModuleDependenciesParams
import java.util.concurrent.CompletableFuture

interface ModuleDependenciesService {
fun syncModuleDependencies(params: SyncModuleDependenciesParams): CompletableFuture<Unit>
}
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.dependencies

import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.module.Module
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.dependencies.SyncModuleDependenciesParams

interface ModuleDependencyProvider {
companion object {
val EP_NAME = ExtensionPointName<ModuleDependencyProvider>("software.aws.toolkits.jetbrains.moduleDependencyProvider")
}

fun isApplicable(module: Module): Boolean
fun createParams(module: Module): SyncModuleDependenciesParams
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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.dependencies.providers

import com.intellij.openapi.module.Module
import com.intellij.openapi.projectRoots.JavaSdkType
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.vfs.VfsUtil
import software.aws.toolkits.jetbrains.services.amazonq.lsp.dependencies.ModuleDependencyProvider
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.dependencies.SyncModuleDependenciesParams

internal class JavaModuleDependencyProvider : ModuleDependencyProvider {
override fun isApplicable(module: Module): Boolean =
ModuleRootManager.getInstance(module).sdk?.sdkType is JavaSdkType

override fun createParams(module: Module): SyncModuleDependenciesParams {
val sourceRoots = getSourceRoots(module)
val dependencies = mutableListOf<String>()

ModuleRootManager.getInstance(module).orderEntries().forEachLibrary { library ->
library.getUrls(OrderRootType.CLASSES).forEach { url ->
dependencies.add(VfsUtil.urlToPath(url))
}
true
}

return SyncModuleDependenciesParams(
moduleName = module.name,
programmingLanguage = "Java",
files = sourceRoots,
dirs = dependencies,
includePatterns = emptyList(),
excludePatterns = emptyList()
)
}

private fun getSourceRoots(module: Module): List<String> =
ModuleRootManager.getInstance(module).contentEntries
.flatMap { contentEntry ->
contentEntry.sourceFolders
.filter { !it.isTestSource }
.mapNotNull { it.file?.path }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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.dependencies.providers

import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ModuleRootManager
import com.jetbrains.python.packaging.management.PythonPackageManager
import com.jetbrains.python.sdk.PythonSdkUtil
import software.aws.toolkits.jetbrains.services.amazonq.lsp.dependencies.ModuleDependencyProvider
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.dependencies.SyncModuleDependenciesParams

internal class PythonModuleDependencyProvider : ModuleDependencyProvider {
override fun isApplicable(module: Module): Boolean =
PythonSdkUtil.findPythonSdk(module) != null

override fun createParams(module: Module): SyncModuleDependenciesParams {
val sourceRoots = getSourceRoots(module)
val dependencies = mutableListOf<String>()

PythonSdkUtil.findPythonSdk(module)?.let { sdk ->
val packageManager = PythonPackageManager.forSdk(module.project, sdk)

Check warning

Code scanning / QDJVMC

Unstable API Usage Warning

'com.jetbrains.python.packaging.management.PythonPackageManager.Companion' is declared in unstable 'com.jetbrains.python.packaging.management.PythonPackageManager' marked with @ApiStatus.Experimental

Check warning

Code scanning / QDJVMC

Unstable API Usage Warning

'forSdk(com.intellij.openapi.project.Project, com.intellij.openapi.projectRoots.Sdk)' is declared in unstable 'com.jetbrains.python.packaging.management.PythonPackageManager' marked with @ApiStatus.Experimental
packageManager.installedPackages.forEach { pkg ->

Check warning

Code scanning / QDJVMC

Unstable API Usage Warning

'getInstalledPackages()' is declared in unstable 'com.jetbrains.python.packaging.management.PythonPackageManager' marked with @ApiStatus.Experimental
dependencies.add(pkg.name)
}
}

return SyncModuleDependenciesParams(
moduleName = module.name,
programmingLanguage = "Python",
files = sourceRoots,
dirs = dependencies,
includePatterns = emptyList(),
excludePatterns = emptyList()
)
}

private fun getSourceRoots(module: Module): List<String> =
ModuleRootManager.getInstance(module).contentEntries
.flatMap { contentEntry ->
contentEntry.sourceFolders
.filter { !it.isTestSource }
.mapNotNull { it.file?.path }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// 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.dependencies

class SyncModuleDependenciesParams(
val moduleName: String,

Check warning

Code scanning / QDJVMC

Unused symbol Warning

Property "moduleName" is never used
val programmingLanguage: String,
val files: List<String>,
val dirs: List<String>,

Check warning

Code scanning / QDJVMC

Unused symbol Warning

Property "dirs" is never used
val includePatterns: List<String>,

Check warning

Code scanning / QDJVMC

Unused symbol Warning

Property "includePatterns" is never used
val excludePatterns: List<String>,

Check warning

Code scanning / QDJVMC

Unused symbol Warning

Property "excludePatterns" is never used
)
Loading
Loading