-
Notifications
You must be signed in to change notification settings - Fork 243
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
feat(amazonq): Implement aws/syncModuleDependencies call #5414
Changes from 15 commits
9d71b4c
1f1c2ac
f92ab4d
58e3ea2
060f64a
63eb25c
f5f4c87
10ddb2d
44c794e
90d5365
9edbfac
b9ba38f
a461559
5ff55ca
b2d5cc3
a1c08a5
f254b7a
c9d7ab3
7b77a6f
3263ac9
7e74c9e
e35e956
d14bfe4
2edaba4
6d9eb85
db03df1
2fd9ee6
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,9 @@ | ||
<!-- Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. --> | ||
<!-- SPDX-License-Identifier: Apache-2.0 --> | ||
|
||
<idea-plugin> | ||
<extensions defaultExtensionNs="com.intellij"> | ||
<moduleDependencyProvider | ||
implementation="software.aws.toolkits.jetbrains.services.amazonq.lsp.dependencies.providers.JavaModuleDependencyProvider"/> | ||
</extensions> | ||
</idea-plugin> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<!-- Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. --> | ||
<!-- SPDX-License-Identifier: Apache-2.0 --> | ||
|
||
<idea-plugin> | ||
<extensions defaultExtensionNs="com.intellij"> | ||
<moduleDependencyProvider | ||
implementation="software.aws.toolkits.jetbrains.services.amazonq.lsp.dependencies.providers.PythonModuleDependencyProvider"/> | ||
</extensions> | ||
</idea-plugin> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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") | ||
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. could you see if |
||
fun syncModuleDependencies(params: SyncModuleDependenciesParams): CompletableFuture<Unit> | ||
|
||
@JsonRequest("aws/credentials/token/update") | ||
fun updateTokenCredentials(payload: UpdateCredentialsPayload): CompletableFuture<ResponseMessage> | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,120 @@ | ||||||||||||||||||||
// 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.Module | ||||||||||||||||||||
|
||||||||||||||||||||
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 com.intellij.openapi.roots.ModuleRootManager | ||||||||||||||||||||
|
||||||||||||||||||||
import com.intellij.openapi.roots.OrderRootType | ||||||||||||||||||||
|
||||||||||||||||||||
import com.intellij.openapi.vfs.VfsUtil | ||||||||||||||||||||
|
||||||||||||||||||||
import com.jetbrains.python.packaging.management.PythonPackageManager | ||||||||||||||||||||
|
||||||||||||||||||||
import com.jetbrains.python.sdk.PythonSdkUtil | ||||||||||||||||||||
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. with this service we should define a generic interface that is registered with the extension point system and register them dynamically through optional depends. otherwise this will all fail if user is missing the java/python plugin
|
||||||||||||||||||||
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")) | ||||||||||||||||||||
} | ||||||||||||||||||||
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. simplifies to
Suggested change
|
||||||||||||||||||||
|
||||||||||||||||||||
private fun syncAllModules() { | ||||||||||||||||||||
ModuleManager.getInstance(project).modules.forEach { module -> | ||||||||||||||||||||
EP_NAME.forEachExtensionSafe { | ||||||||||||||||||||
if (it.isApplicable(module)) { | ||||||||||||||||||||
syncModuleDependencies(it.createParams(module)) | ||||||||||||||||||||
return@forEachExtensionSafe | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
private fun getSourceRoots(module: Module): List<String> { | ||||||||||||||||||||
val sourceRoots = mutableListOf<String>() | ||||||||||||||||||||
|
||||||||||||||||||||
// Get all source roots from content entries | ||||||||||||||||||||
ModuleRootManager.getInstance(module).contentEntries | ||||||||||||||||||||
.flatMap { contentEntry -> | ||||||||||||||||||||
contentEntry.sourceFolders | ||||||||||||||||||||
.filter { !it.isTestSource } | ||||||||||||||||||||
.mapNotNull { it.file?.path } | ||||||||||||||||||||
} | ||||||||||||||||||||
.forEach { path -> sourceRoots.add(path) } | ||||||||||||||||||||
|
||||||||||||||||||||
return sourceRoots | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
private fun createJavaParams(module: Module): SyncModuleDependenciesParams { | ||||||||||||||||||||
Check warning on line 77 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/dependencies/DefaultModuleDependenciesService.kt
|
||||||||||||||||||||
|
||||||||||||||||||||
val sourceRoots = getSourceRoots(module) | ||||||||||||||||||||
val dependencies = mutableListOf<String>() | ||||||||||||||||||||
|
||||||||||||||||||||
// Get library dependencies | ||||||||||||||||||||
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 createPythonParams(module: Module): SyncModuleDependenciesParams { | ||||||||||||||||||||
Check warning on line 99 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/dependencies/DefaultModuleDependenciesService.kt
|
||||||||||||||||||||
|
||||||||||||||||||||
val sourceRoots = getSourceRoots(module) | ||||||||||||||||||||
val dependencies = mutableListOf<String>() | ||||||||||||||||||||
|
||||||||||||||||||||
// Get Python packages | ||||||||||||||||||||
PythonSdkUtil.findPythonSdk(module)?.let { sdk -> | ||||||||||||||||||||
val packageManager = PythonPackageManager.forSdk(module.project, sdk) | ||||||||||||||||||||
Check warning on line 105 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/dependencies/DefaultModuleDependenciesService.kt
|
||||||||||||||||||||
|
||||||||||||||||||||
packageManager.installedPackages.forEach { pkg -> | ||||||||||||||||||||
Check warning on line 106 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/dependencies/DefaultModuleDependenciesService.kt
|
||||||||||||||||||||
|
||||||||||||||||||||
dependencies.add(pkg.name) | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
return SyncModuleDependenciesParams( | ||||||||||||||||||||
moduleName = module.name, | ||||||||||||||||||||
programmingLanguage = "Python", | ||||||||||||||||||||
files = sourceRoots, | ||||||||||||||||||||
dirs = dependencies, | ||||||||||||||||||||
includePatterns = emptyList(), | ||||||||||||||||||||
excludePatterns = emptyList() | ||||||||||||||||||||
) | ||||||||||||||||||||
} | ||||||||||||||||||||
} |
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>("com.intellij.moduleDependencyProvider") | ||
} | ||
|
||
fun isApplicable(module: Module): Boolean | ||
fun createParams(module: Module): SyncModuleDependenciesParams | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// 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 | ||
|
||
class JavaModuleDependencyProvider : ModuleDependencyProvider { | ||
Check warning on line 14 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/dependencies/providers/JavaModuleDependencyProvider.kt
|
||
|
||
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> { | ||
return 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,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.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 | ||
|
||
class PythonModuleDependencyProvider : ModuleDependencyProvider { | ||
Check warning on line 13 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/dependencies/providers/PythonModuleDependencyProvider.kt
|
||
|
||
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 on line 22 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/dependencies/providers/PythonModuleDependencyProvider.kt
|
||
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 on line 23 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/dependencies/providers/PythonModuleDependencyProvider.kt
|
||
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> { | ||
return 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 | ||
|
||
public class SyncModuleDependenciesParams( | ||
Check warning on line 6 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/dependencies/SyncModuleDependenciesParams.kt
|
||
|
||
val moduleName: String, | ||
Check warning on line 7 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/dependencies/SyncModuleDependenciesParams.kt
|
||
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 on line 10 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/dependencies/SyncModuleDependenciesParams.kt
|
||
Check warning Code scanning / QDJVMC Unused symbol Warning
Property "dirs" is never used
|
||
val includePatterns: List<String>, | ||
Check warning on line 11 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/dependencies/SyncModuleDependenciesParams.kt
|
||
Check warning Code scanning / QDJVMC Unused symbol Warning
Property "includePatterns" is never used
|
||
val excludePatterns: List<String>, | ||
Check warning on line 12 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/dependencies/SyncModuleDependenciesParams.kt
|
||
Check warning Code scanning / QDJVMC Unused symbol Warning
Property "excludePatterns" is never used
|
||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// 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.util | ||
|
||
import com.intellij.diagnostic.PluginException | ||
import com.intellij.openapi.diagnostic.Logger | ||
import com.intellij.openapi.extensions.ExtensionPointName | ||
|
||
val LOG = Logger.getInstance("software.aws.toolkits.jetbrains.services.amazonq.lsp.util.ExtensionPointUtils") | ||
|
||
inline fun <T : Any> ExtensionPointName<T>.forEachExtensionSafe(action: (T) -> Unit) { | ||
|
||
for (extension in extensionList) { | ||
try { | ||
action(extension) | ||
} catch (e: PluginException) { | ||
LOG.warn("Failed to process extension ${extension::class.java.name}", e) | ||
} catch (e: Exception) { | ||
LOG.warn("Error processing extension ${extension::class.java.name}", e) | ||
} | ||
} | ||
} |
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.
depends should be on top-level plugin.xml, so use existing
amazon-ext-java.xml