From 2b2c4d3f7b8cfe8519916d302c464cbf84dbc6cc Mon Sep 17 00:00:00 2001 From: Richard Li Date: Mon, 19 May 2025 17:38:17 -0700 Subject: [PATCH 1/2] fix(amazonq): fix code issues tab not appearing in remote on remote, each client gets its own instance of the tool window. logic needs to handle this case, otherwise it will only make changes against the 'main' IDE, and will not be visible to remote clients --- .../codescan/CodeWhispererCodeScanManager.kt | 108 +++++++++++------- .../codescan/CodeWhispererCodeScanSession.kt | 3 +- .../jetbrains/DefaultProblemsViewMutator.kt | 14 +++ .../toolkits/jetbrains/ProblemsViewMutator.kt | 16 +++ .../jetbrains/CwmProblemsViewMutator.kt | 20 ++++ .../META-INF/amazonq-ext-codewithme.xml | 8 ++ .../src/main/resources/META-INF/plugin.xml | 10 ++ 7 files changed, 139 insertions(+), 40 deletions(-) create mode 100644 plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/DefaultProblemsViewMutator.kt create mode 100644 plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/ProblemsViewMutator.kt create mode 100644 plugins/amazonq/shared/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/CwmProblemsViewMutator.kt create mode 100644 plugins/amazonq/src/main/resources/META-INF/amazonq-ext-codewithme.xml diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanManager.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanManager.kt index 674bcdf9500..6cc60550487 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanManager.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanManager.kt @@ -3,13 +3,13 @@ package software.aws.toolkits.jetbrains.services.codewhisperer.codescan -import com.intellij.analysis.problemsView.toolWindow.ProblemsView import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInspection.util.InspectionMessage import com.intellij.icons.AllIcons import com.intellij.lang.Commenter import com.intellij.lang.Language import com.intellij.lang.LanguageCommenters +import com.intellij.openapi.Disposable import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys @@ -36,8 +36,10 @@ import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.TextRange import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.isFile +import com.intellij.openapi.wm.ToolWindow import com.intellij.psi.PsiDocumentManager import com.intellij.refactoring.suggested.range +import com.intellij.ui.content.Content import com.intellij.ui.content.ContentManagerEvent import com.intellij.ui.content.ContentManagerListener import com.intellij.ui.treeStructure.Tree @@ -60,6 +62,7 @@ import software.aws.toolkits.core.utils.error import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.core.utils.info import software.aws.toolkits.core.utils.warn +import software.aws.toolkits.jetbrains.ProblemsViewMutator import software.aws.toolkits.jetbrains.core.coroutines.EDT import software.aws.toolkits.jetbrains.core.coroutines.getCoroutineUiContext import software.aws.toolkits.jetbrains.core.coroutines.projectCoroutineScope @@ -107,26 +110,10 @@ import kotlin.coroutines.CoroutineContext private val LOG = getLogger() -class CodeWhispererCodeScanManager(val project: Project) { - private val defaultScope = projectCoroutineScope(project) +class CodeWhispererCodeScanManager(val project: Project, private val defaultScope: CoroutineScope) : Disposable { private val codeScanResultsPanel by lazy { CodeWhispererCodeScanResultsView(project, defaultScope) } - private val codeScanIssuesContent by lazy { - val contentManager = getProblemsWindow().contentManager - contentManager.factory.createContent( - codeScanResultsPanel, - message("codewhisperer.codescan.scan_display"), - false - ).also { - Disposer.register(contentManager, it) - contentManager.addContentManagerListener(object : ContentManagerListener { - override fun contentRemoved(event: ContentManagerEvent) { - if (event.content == it) reset() - } - }) - } - } private var autoScanIssues = emptyList() private var ondemandScanIssues = emptyList() @@ -507,12 +494,19 @@ class CodeWhispererCodeScanManager(val project: Project) { private fun refreshUi() { val codeScanTreeModel = CodeWhispererCodeScanTreeModel(codeScanTreeNodeRoot) val totalIssuesCount = codeScanTreeModel.getTotalIssuesCount() - if (totalIssuesCount > 0) { - codeScanIssuesContent.displayName = - message("codewhisperer.codescan.scan_display_with_issues", totalIssuesCount, INACTIVE_TEXT_COLOR) + val displayName = if (totalIssuesCount > 0) { + message("codewhisperer.codescan.scan_display_with_issues", totalIssuesCount, INACTIVE_TEXT_COLOR) } else { - codeScanIssuesContent.displayName = message("codewhisperer.codescan.scan_display") + message("codewhisperer.codescan.scan_display") } + + withToolWindow { window -> + window.contentManager.contents.filter { it.isCodeScanView() } + .forEach { + it.displayName = displayName + } + } + codeScanResultsPanel.refreshUIWithUpdatedModel(codeScanTreeModel) } @@ -650,10 +644,25 @@ class CodeWhispererCodeScanManager(val project: Project) { * This method adds code content to the problems view if not already added. */ fun buildCodeScanUI() = runInEdt { - val problemsWindow = getProblemsWindow() - if (!problemsWindow.contentManager.contents.contains(codeScanIssuesContent)) { - problemsWindow.contentManager.addContent(codeScanIssuesContent) - codeScanIssuesContent.displayName = message("codewhisperer.codescan.scan_display") + withToolWindow { problemsWindow -> + val contentManager = problemsWindow.contentManager + if (!contentManager.contents.any { it.isCodeScanView() }) { + contentManager.addContent( + contentManager.factory.createContent( + codeScanResultsPanel, + message("codewhisperer.codescan.scan_display"), + false + ).also { + it.tabName = message("codewhisperer.codescan.scan_display") + Disposer.register(contentManager, it) + contentManager.addContentManagerListener(object : ContentManagerListener { + override fun contentRemoved(event: ContentManagerEvent) { + if (event.content == it) reset() + } + }) + } + ) + } } } @@ -661,15 +670,19 @@ class CodeWhispererCodeScanManager(val project: Project) { * This method shows the code content panel in problems view */ fun showCodeScanUI() = runInEdt { - val problemsWindow = getProblemsWindow() - problemsWindow.contentManager.setSelectedContent(codeScanIssuesContent) - problemsWindow.show() + withToolWindow { problemsWindow -> + problemsWindow.contentManager.contents.firstOrNull { it.isCodeScanView() } + ?.let { problemsWindow.contentManager.setSelectedContent(it) } + problemsWindow.show() + } } fun removeCodeScanUI() = runInEdt { - val problemsWindow = getProblemsWindow() - if (problemsWindow.contentManager.contents.contains(codeScanIssuesContent)) { - problemsWindow.contentManager.removeContent(codeScanIssuesContent, false) + withToolWindow { problemsWindow -> + problemsWindow.contentManager.contents.filter { it.isCodeScanView() } + .forEach { + problemsWindow.contentManager.removeContent(it, true) + } } } @@ -735,8 +748,13 @@ class CodeWhispererCodeScanManager(val project: Project) { ) } - private fun getProblemsWindow() = ProblemsView.getToolWindow(project) - ?: error(message("codewhisperer.codescan.problems_window_not_found")) + private fun withToolWindow(runnable: (ToolWindow) -> Unit) { + ProblemsViewMutator.EP.forEachExtensionSafe { mutator -> + mutator.mutateProblemsView(project, runnable) + } + } + + private fun Content.isCodeScanView() = component == codeScanResultsPanel private fun reset() = runInEdt { // clear the codeScanTreeNodeRoot @@ -765,7 +783,7 @@ class CodeWhispererCodeScanManager(val project: Project) { editorFactory.addEditorFactoryListener(fileListener, project) editorFactory.eventMulticaster.addEditorMouseMotionListener( editorMouseListener, - codeScanIssuesContent + this ) } } @@ -955,8 +973,12 @@ class CodeWhispererCodeScanManager(val project: Project) { val codeScanTreeModel = CodeWhispererCodeScanTreeModel(codeScanTreeNodeRoot) val totalIssuesCount = codeScanTreeModel.getTotalIssuesCount() if (totalIssuesCount > 0) { - codeScanIssuesContent.displayName = - message("codewhisperer.codescan.scan_display_with_issues", totalIssuesCount, INACTIVE_TEXT_COLOR) + withToolWindow { + it.contentManager.contents.filter { it.isCodeScanView() } + .forEach { + it.displayName = message("codewhisperer.codescan.scan_display_with_issues", totalIssuesCount, INACTIVE_TEXT_COLOR) + } + } } codeScanResultsPanel.refreshUIWithUpdatedModel(codeScanTreeModel) } @@ -971,8 +993,13 @@ class CodeWhispererCodeScanManager(val project: Project) { val codeScanTreeModel = CodeWhispererCodeScanTreeModel(root) val totalIssuesCount = codeScanTreeModel.getTotalIssuesCount() if (totalIssuesCount > 0) { - codeScanIssuesContent.displayName = - message("codewhisperer.codescan.scan_display_with_issues", totalIssuesCount, INACTIVE_TEXT_COLOR) + withToolWindow { window -> + window.contentManager.contents.filter { it.isCodeScanView() } + .forEach { + it.displayName = + message("codewhisperer.codescan.scan_display_with_issues", totalIssuesCount, INACTIVE_TEXT_COLOR) + } + } } codeScanResultsPanel.updateAndDisplayScanResults(codeScanTreeModel, scannedFiles, scope) } @@ -1004,6 +1031,9 @@ class CodeWhispererCodeScanManager(val project: Project) { return isInsideWorkTree(projectDir) } + override fun dispose() { + } + companion object { fun getInstance(project: Project): CodeWhispererCodeScanManager = project.service() } diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanSession.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanSession.kt index ec3b1c30df7..65ef44f18dc 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanSession.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanSession.kt @@ -31,6 +31,7 @@ import software.amazon.awssdk.services.codewhispererruntime.model.StartCodeAnaly import software.amazon.awssdk.services.codewhispererruntime.model.StartCodeAnalysisResponse import software.aws.toolkits.core.utils.Waiters.waitUntil import software.aws.toolkits.core.utils.debug +import software.aws.toolkits.core.utils.error import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.core.utils.info import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager @@ -291,7 +292,7 @@ class CodeWhispererCodeScanSession(val sessionContext: CodeScanSessionContext) { .build() ) } catch (e: Exception) { - LOG.debug { "Getting code review failed: ${e.message}" } + LOG.error(e) { "Getting code review failed: ${e.message}" } val errorMessage = getTelemetryErrorMessage(e, featureUseCase = CodeWhispererConstants.FeatureName.CODE_REVIEW) throw codeScanServerException("GetCodeReviewException: $errorMessage") } diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/DefaultProblemsViewMutator.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/DefaultProblemsViewMutator.kt new file mode 100644 index 00000000000..6d4702bfbc0 --- /dev/null +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/DefaultProblemsViewMutator.kt @@ -0,0 +1,14 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains + +import com.intellij.analysis.problemsView.toolWindow.ProblemsView +import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.ToolWindow + +class DefaultProblemsViewMutator : ProblemsViewMutator { + override fun mutateProblemsView(project: Project, runnable: (ToolWindow) -> Unit) { + ProblemsView.getToolWindow(project)?.let { runnable(it) } + } +} diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/ProblemsViewMutator.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/ProblemsViewMutator.kt new file mode 100644 index 00000000000..4da60db9d64 --- /dev/null +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/ProblemsViewMutator.kt @@ -0,0 +1,16 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains + +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.ToolWindow + +interface ProblemsViewMutator { + fun mutateProblemsView(project: Project, runnable: (ToolWindow) -> Unit) + + companion object { + val EP = ExtensionPointName("software.aws.toolkits.jetbrains.problemsViewMutator") + } +} diff --git a/plugins/amazonq/shared/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/CwmProblemsViewMutator.kt b/plugins/amazonq/shared/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/CwmProblemsViewMutator.kt new file mode 100644 index 00000000000..da94b2f40de --- /dev/null +++ b/plugins/amazonq/shared/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/CwmProblemsViewMutator.kt @@ -0,0 +1,20 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains + +import com.intellij.analysis.problemsView.toolWindow.ProblemsView +import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.ToolWindow +import com.jetbrains.rdserver.toolWindow.BackendToolWindowHost + +class CwmProblemsViewMutator : ProblemsViewMutator { + override fun mutateProblemsView(project: Project, runnable: (ToolWindow) -> Unit) { + BackendToolWindowHost.getAllInstances(project).forEach { host -> + host.getToolWindow(ProblemsView.ID)?.let { + runnable(it) + } + + } + } +} diff --git a/plugins/amazonq/src/main/resources/META-INF/amazonq-ext-codewithme.xml b/plugins/amazonq/src/main/resources/META-INF/amazonq-ext-codewithme.xml new file mode 100644 index 00000000000..d199d28324c --- /dev/null +++ b/plugins/amazonq/src/main/resources/META-INF/amazonq-ext-codewithme.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugins/amazonq/src/main/resources/META-INF/plugin.xml b/plugins/amazonq/src/main/resources/META-INF/plugin.xml index 786974563a0..bea85c6ab6a 100644 --- a/plugins/amazonq/src/main/resources/META-INF/plugin.xml +++ b/plugins/amazonq/src/main/resources/META-INF/plugin.xml @@ -58,6 +58,8 @@ aws.toolkit.core com.intellij.modules.lang + com.jetbrains.codeWithMe + Dart com.intellij.database org.jetbrains.plugins.go @@ -97,8 +99,16 @@ + + + + + + From ee16d5f0667ff886cc98dcd0058b5b874b2ed1c9 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 20 May 2025 15:14:05 -0700 Subject: [PATCH 2/2] tst --- .../software/aws/toolkits/gradle/intellij/IdeVersions.kt | 5 ++++- .../codewhisperer/codescan/CodeWhispererCodeScanManager.kt | 4 ++-- .../aws/toolkits/jetbrains/CwmProblemsViewMutator.kt | 1 - 3 files changed, 6 insertions(+), 4 deletions(-) rename plugins/amazonq/shared/{jetbrains-ultimate => jetbrains-community}/src/software/aws/toolkits/jetbrains/CwmProblemsViewMutator.kt (99%) diff --git a/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/intellij/IdeVersions.kt b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/intellij/IdeVersions.kt index e0048684766..de331fa4415 100644 --- a/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/intellij/IdeVersions.kt +++ b/buildSrc/src/main/kotlin/software/aws/toolkits/gradle/intellij/IdeVersions.kt @@ -22,7 +22,7 @@ object IdeVersions { private val commonPlugins = listOf( "Git4Idea", "org.jetbrains.plugins.terminal", - "org.jetbrains.plugins.yaml" + "org.jetbrains.plugins.yaml", ) private val ideProfiles = listOf( @@ -34,6 +34,7 @@ object IdeVersions { "com.intellij.java", "com.intellij.gradle", "org.jetbrains.idea.maven", + "com.jetbrains.codeWithMe", ), marketplacePlugins = listOf( "org.toml.lang:242.20224.155", @@ -71,6 +72,7 @@ object IdeVersions { "com.intellij.java", "com.intellij.gradle", "org.jetbrains.idea.maven", + "com.jetbrains.codeWithMe", ), marketplacePlugins = listOf( "org.toml.lang:243.21565.122", @@ -112,6 +114,7 @@ object IdeVersions { "com.intellij.java", "com.intellij.gradle", "org.jetbrains.idea.maven", + "com.jetbrains.codeWithMe", ), marketplacePlugins = listOf( "PythonCore:251.23774.460", diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanManager.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanManager.kt index 6cc60550487..7fe6d8f1535 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanManager.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanManager.kt @@ -973,8 +973,8 @@ class CodeWhispererCodeScanManager(val project: Project, private val defaultScop val codeScanTreeModel = CodeWhispererCodeScanTreeModel(codeScanTreeNodeRoot) val totalIssuesCount = codeScanTreeModel.getTotalIssuesCount() if (totalIssuesCount > 0) { - withToolWindow { - it.contentManager.contents.filter { it.isCodeScanView() } + withToolWindow { problemsWindow -> + problemsWindow.contentManager.contents.filter { it.isCodeScanView() } .forEach { it.displayName = message("codewhisperer.codescan.scan_display_with_issues", totalIssuesCount, INACTIVE_TEXT_COLOR) } diff --git a/plugins/amazonq/shared/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/CwmProblemsViewMutator.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/CwmProblemsViewMutator.kt similarity index 99% rename from plugins/amazonq/shared/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/CwmProblemsViewMutator.kt rename to plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/CwmProblemsViewMutator.kt index da94b2f40de..3346352e7ed 100644 --- a/plugins/amazonq/shared/jetbrains-ultimate/src/software/aws/toolkits/jetbrains/CwmProblemsViewMutator.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/CwmProblemsViewMutator.kt @@ -14,7 +14,6 @@ class CwmProblemsViewMutator : ProblemsViewMutator { host.getToolWindow(ProblemsView.ID)?.let { runnable(it) } - } } }