Skip to content

Commit 18f1567

Browse files
committed
Merge remote-tracking branch 'origin/feature/q-lsp-chat' into samgst/q-chat-copy-to-clipboard
# Conflicts: # plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt # plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageServer.kt # plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/ChatNotification.kt # plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt
2 parents 46c12b6 + d215249 commit 18f1567

File tree

18 files changed

+163
-125
lines changed

18 files changed

+163
-125
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "bugfix",
3+
"description" : "/review disable auto scan by default"
4+
}

.changes/next-release/feature-ae7d15f4-ae07-4faf-963a-6d83a85852e4.json

Lines changed: 0 additions & 4 deletions
This file was deleted.

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged
1717
import kotlinx.coroutines.flow.launchIn
1818
import kotlinx.coroutines.flow.merge
1919
import kotlinx.coroutines.flow.onEach
20-
import kotlinx.coroutines.future.await
2120
import kotlinx.coroutines.launch
2221
import org.cef.browser.CefBrowser
2322
import org.eclipse.lsp4j.Position
@@ -30,7 +29,10 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.encryption.JwtEncryp
3029
import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ChatCommunicationManager
3130
import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.getTextDocumentIdentifier
3231
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_COPY_CODE_TO_CLIPBOARD
32+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_INFO_LINK_CLICK
33+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_LINK_CLICK
3334
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_QUICK_ACTION
35+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_SOURCE_LINK_CLICK
3436
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatNotification
3537
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatParams
3638
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatPrompt
@@ -39,9 +41,15 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CopyC
3941
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CursorState
4042
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.EncryptedChatParams
4143
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.EncryptedQuickActionChatParams
44+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.InfoLinkClickNotification
45+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.InfoLinkClickParams
46+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.LinkClickNotification
47+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.LinkClickParams
4248
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.QuickChatActionRequest
4349
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SEND_CHAT_COMMAND_PROMPT
4450
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SendChatPromptRequest
51+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SourceLinkClickNotification
52+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SourceLinkClickParams
4553
import software.aws.toolkits.jetbrains.services.amazonq.util.command
4654
import software.aws.toolkits.jetbrains.services.amazonq.util.tabType
4755
import software.aws.toolkits.jetbrains.services.amazonq.webview.theme.AmazonQTheme
@@ -210,6 +218,23 @@ class BrowserConnector(
210218

211219
showResult(result, partialResultToken, tabId, encryptionManager, browser)
212220
}
221+
CHAT_LINK_CLICK -> {
222+
handleChatNotification<LinkClickNotification, LinkClickParams>(node) { server, params ->
223+
server.linkClick(params)
224+
}
225+
}
226+
227+
CHAT_INFO_LINK_CLICK -> {
228+
handleChatNotification<InfoLinkClickNotification, InfoLinkClickParams>(node) { server, params ->
229+
server.infoLinkClick(params)
230+
}
231+
}
232+
233+
CHAT_SOURCE_LINK_CLICK -> {
234+
handleChatNotification<SourceLinkClickNotification, SourceLinkClickParams>(node) { server, params ->
235+
server.sourceLinkClick(params)
236+
}
237+
}
213238
CHAT_COPY_CODE_TO_CLIPBOARD -> {
214239
handleChatNotification<CopyCodeToClipboardNotification, CopyCodeToClipboardParams>(node) { server, params ->
215240
server.copyCodeToClipboard(params)
@@ -234,7 +259,6 @@ class BrowserConnector(
234259
isPartialResult = false
235260
)
236261
browser.postChat(messageToChat)
237-
showResult(result, partialResultToken, tabId, encryptionManager, browser)
238262
}
239263
}
240264

plugins/amazonq/codewhisperer/jetbrains-community/src/migration/software/aws/toolkits/jetbrains/services/codewhisperer/explorer/CodeWhispererExplorerActionManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class CodeWhispererExplorerActionManager : PersistentStateComponent<CodeWhispere
4848
actionState.value[CodeWhispererExploreStateType.IsAutoEnabled] = isAutoEnabled
4949
}
5050

51-
fun isAutoEnabledForCodeScan(): Boolean = actionState.value.getOrDefault(CodeWhispererExploreStateType.IsAutoCodeScanEnabled, true)
51+
fun isAutoEnabledForCodeScan(): Boolean = actionState.value.getOrDefault(CodeWhispererExploreStateType.IsAutoCodeScanEnabled, false)
5252

5353
fun setAutoEnabledForCodeScan(isAutoEnabledForCodeScan: Boolean) {
5454
actionState.value[CodeWhispererExploreStateType.IsAutoCodeScanEnabled] = isAutoEnabledForCodeScan

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/credentials/CodeWhispererClientAdaptor.kt

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import software.amazon.awssdk.services.codewhispererruntime.model.UserIntent
3838
import software.aws.toolkits.core.utils.debug
3939
import software.aws.toolkits.core.utils.getLogger
4040
import software.aws.toolkits.jetbrains.services.amazonq.codeWhispererUserContext
41-
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfile
4241
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager
4342
import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererCustomization
4443
import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage
@@ -79,7 +78,7 @@ interface CodeWhispererClientAdaptor {
7978

8079
fun getCodeFixJob(request: GetCodeFixJobRequest): GetCodeFixJobResponse
8180

82-
fun listAvailableCustomizations(profile: QRegionProfile): List<CodeWhispererCustomization>
81+
fun listAvailableCustomizations(): List<CodeWhispererCustomization>
8382

8483
fun startTestGeneration(uploadId: String, targetCode: List<TargetCode>, userInput: String): StartTestGenerationResponse
8584

@@ -283,9 +282,9 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW
283282
override fun getCodeFixJob(request: GetCodeFixJobRequest): GetCodeFixJobResponse = bearerClient().getCodeFixJob(request)
284283

285284
// DO NOT directly use this method to fetch customizations, use wrapper [CodeWhispererModelConfigurator.listCustomization()] instead
286-
override fun listAvailableCustomizations(profile: QRegionProfile): List<CodeWhispererCustomization> =
287-
QRegionProfileManager.getInstance().getQClient<CodeWhispererRuntimeClient>(project, profile).listAvailableCustomizationsPaginator(
288-
ListAvailableCustomizationsRequest.builder().profileArn(profile.arn).build()
285+
override fun listAvailableCustomizations(): List<CodeWhispererCustomization> =
286+
bearerClient().listAvailableCustomizationsPaginator(
287+
ListAvailableCustomizationsRequest.builder().profileArn(QRegionProfileManager.getInstance().activeProfile(project)?.arn).build()
289288
)
290289
.stream()
291290
.toList()
@@ -299,8 +298,7 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW
299298
CodeWhispererCustomization(
300299
arn = it.arn(),
301300
name = it.name(),
302-
description = it.description(),
303-
profile = profile
301+
description = it.description()
304302
)
305303
}
306304
}

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/customization/CodeWhispererCustomizationDialog.kt

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ import software.amazon.awssdk.arns.Arn
2626
import software.aws.toolkits.core.utils.debug
2727
import software.aws.toolkits.core.utils.getLogger
2828
import software.aws.toolkits.core.utils.tryOrNull
29-
import software.aws.toolkits.jetbrains.services.amazonq.profile.QProfileSwitchIntent
30-
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfile
31-
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager
3229
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.Q_CUSTOM_LEARN_MORE_URI
3330
import software.aws.toolkits.jetbrains.ui.AsyncComboBox
3431
import software.aws.toolkits.jetbrains.utils.notifyInfo
@@ -37,7 +34,7 @@ import javax.swing.JComponent
3734
import javax.swing.JList
3835

3936
private val NoDataToDisplay = CustomizationUiItem(
40-
CodeWhispererCustomization("", message("codewhisperer.custom.dialog.option.no_data"), "", QRegionProfile("", "")),
37+
CodeWhispererCustomization("", message("codewhisperer.custom.dialog.option.no_data"), ""),
4138
false,
4239
false
4340
)
@@ -109,15 +106,6 @@ class CodeWhispererCustomizationDialog(
109106
RadioButtonOption.Customization -> run {
110107
CodeWhispererModelConfigurator.getInstance().switchCustomization(project, modal.selectedCustomization?.customization)
111108
notifyCustomizationIsSelected(project, modal.selectedCustomization)
112-
// Switch profile if it doesn't match the customization's profile.
113-
// Customizations are profile-scoped and must be used under the correct context.
114-
if (modal.selectedCustomization?.customization?.profile?.arn != QRegionProfileManager.getInstance().activeProfile(project)?.arn) {
115-
QRegionProfileManager.getInstance().switchProfile(
116-
project,
117-
modal.selectedCustomization?.customization?.profile,
118-
QProfileSwitchIntent.Customization
119-
)
120-
}
121109
}
122110
}
123111

@@ -191,14 +179,12 @@ class CodeWhispererCustomizationDialog(
191179
proposeModelUpdate { model ->
192180
val activeCustomization = CodeWhispererModelConfigurator.getInstance().activeCustomization(project)
193181
val unsorted = myCustomizations ?: CodeWhispererModelConfigurator.getInstance().listCustomizations(project).orEmpty()
194-
val activeProfile = QRegionProfileManager.getInstance().activeProfile(project)
195-
// Group customizations by profile name (active profile first, then alphabetical), with the active customization on top
196-
val sorted = unsorted.sortedWith(
197-
compareBy<CustomizationUiItem> { it.customization.profile?.profileName != activeProfile?.profileName }
198-
.thenBy { it.customization.profile?.profileName.orEmpty() }
199-
.thenBy { it.customization.name }
200-
)
201-
.let { list -> activeCustomization?.let { list.putPickedUpFront(setOf(it)) } ?: list }
182+
183+
val sorted = activeCustomization?.let {
184+
unsorted.putPickedUpFront(setOf(it))
185+
} ?: run {
186+
unsorted.sortedBy { it.customization.name }
187+
}
202188

203189
if (
204190
sorted.isNotEmpty() &&
@@ -273,10 +259,6 @@ private object CustomizationRenderer : ColoredListCellRenderer<CustomizationUiIt
273259
}
274260
}
275261

276-
if (it.customization.profile?.profileName?.isNotEmpty() == true) {
277-
append(" [${it.customization.profile?.profileName}]", SimpleTextAttributes.REGULAR_ATTRIBUTES)
278-
}
279-
280262
if (it.isNew) {
281263
append(" New", SimpleTextAttributes.GRAYED_SMALL_ATTRIBUTES)
282264
}

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/customization/CodeWhispererModelConfigurator.kt

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ import software.aws.toolkits.core.utils.getLogger
2323
import software.aws.toolkits.jetbrains.services.amazonq.CodeWhispererFeatureConfigService
2424
import software.aws.toolkits.jetbrains.services.amazonq.calculateIfIamIdentityCenterConnection
2525
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfile
26-
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager
2726
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileSelectedListener
2827
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor
28+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
2929
import software.aws.toolkits.jetbrains.utils.notifyInfo
3030
import software.aws.toolkits.jetbrains.utils.notifyWarn
3131
import software.aws.toolkits.jetbrains.utils.pluginAwareExecuteOnPooledThread
@@ -108,24 +108,25 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
108108
@RequiresBackgroundThread
109109
override fun listCustomizations(project: Project, passive: Boolean): List<CustomizationUiItem>? =
110110
calculateIfIamIdentityCenterConnection(project) {
111-
// 1. fetch all profiles, invoke fetch customizations API and get result for each profile and aggregate all the results
112-
val listAvailableProfilesResult = QRegionProfileManager.getInstance().listRegionProfiles(project)
113-
?: error("Attempted to fetch profiles while there does not exist")
114-
115-
val aggregatedCustomizations = listAvailableProfilesResult.flatMap { profile ->
116-
runCatching {
117-
CodeWhispererClientAdaptor.getInstance(project).listAvailableCustomizations(profile)
118-
}.onFailure { e ->
119-
val requestId = (e as? CodeWhispererRuntimeException)?.requestId()
120-
val logMessage = "ListAvailableCustomizations: failed due to unknown error ${e.message}, " +
121-
"requestId: ${requestId.orEmpty()}, profileName: ${profile.profileName}"
122-
LOG.debug { logMessage }
123-
}.getOrDefault(emptyList())
111+
// 1. invoke API and get result
112+
val listAvailableCustomizationsResult = try {
113+
CodeWhispererClientAdaptor.getInstance(project).listAvailableCustomizations()
114+
} catch (e: Exception) {
115+
val requestId = (e as? CodeWhispererRuntimeException)?.requestId()
116+
val logMessage = if (CodeWhispererConstants.Customization.noAccessToCustomizationExceptionPredicate(e)) {
117+
// TODO: not required for non GP users
118+
"ListAvailableCustomizations: connection ${it.id} is not allowlisted, requestId: ${requestId.orEmpty()}"
119+
} else {
120+
"ListAvailableCustomizations: failed due to unknown error ${e.message}, requestId: ${requestId.orEmpty()}"
121+
}
122+
123+
LOG.debug { logMessage }
124+
null
124125
}
125126

126127
// 2. get diff
127128
val previousCustomizationsShapshot = connectionToCustomizationsShownLastTime.getOrElse(it.id) { emptyList() }
128-
val diff = aggregatedCustomizations.filterNot { customization -> previousCustomizationsShapshot.contains(customization.arn) }.toSet()
129+
val diff = listAvailableCustomizationsResult?.filterNot { customization -> previousCustomizationsShapshot.contains(customization.arn) }?.toSet()
129130

130131
// 3 if passive,
131132
// (1) update allowlisting
@@ -134,36 +135,42 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
134135
// if not passive,
135136
// (1) update the customization list snapshot (seen by users last time) if it will be displayed
136137
if (passive) {
137-
connectionIdToIsAllowlisted[it.id] = aggregatedCustomizations.isNotEmpty()
138-
if (diff.isNotEmpty() && !hasShownNewCustomizationNotification.getAndSet(true)) {
138+
connectionIdToIsAllowlisted[it.id] = listAvailableCustomizationsResult != null
139+
if (diff?.isNotEmpty() == true && !hasShownNewCustomizationNotification.getAndSet(true)) {
139140
notifyNewCustomization(project)
140141
}
141142
} else {
142-
connectionToCustomizationsShownLastTime[it.id] = aggregatedCustomizations.map { customization -> customization.arn }.toMutableList()
143+
listAvailableCustomizationsResult?.let { customizations ->
144+
connectionToCustomizationsShownLastTime[it.id] = customizations.map { customization -> customization.arn }.toMutableList()
145+
}
143146
}
144147

145148
// 4. invalidate selected customization if
146149
// (1) the API call failed
147150
// (2) the selected customization is not in the resultset of API call
148-
// (3) the existing q region profile associated with the selected customization does not match the currently active profile
149151
activeCustomization(project)?.let { activeCustom ->
150-
if (aggregatedCustomizations.isEmpty()) {
152+
if (listAvailableCustomizationsResult == null) {
151153
invalidateSelectedAndNotify(project)
152-
} else if (!aggregatedCustomizations.any { latestCustom -> latestCustom.arn == activeCustom.arn } ||
153-
(activeCustom.profile != null && activeCustom.profile != QRegionProfileManager.getInstance().activeProfile(project))
154-
) {
154+
} else if (!listAvailableCustomizationsResult.any { latestCustom -> latestCustom.arn == activeCustom.arn }) {
155155
invalidateSelectedAndNotify(project)
156156
}
157157
}
158158

159159
// 5. transform result to UI items and return
160-
val nameToCount = aggregatedCustomizations.groupingBy { customization -> customization.name }.eachCount()
161-
val customizationUiItems = aggregatedCustomizations.map { customization ->
162-
CustomizationUiItem(
163-
customization,
164-
isNew = diff.contains(customization),
165-
shouldPrefixAccountId = (nameToCount[customization.name] ?: 0) > 1
166-
)
160+
val customizationUiItems = if (diff != null) {
161+
listAvailableCustomizationsResult.let { customizations ->
162+
val nameToCount = customizations.groupingBy { customization -> customization.name }.eachCount()
163+
164+
customizations.map { customization ->
165+
CustomizationUiItem(
166+
customization,
167+
isNew = diff.contains(customization),
168+
shouldPrefixAccountId = (nameToCount[customization.name] ?: 0) > 1
169+
)
170+
}
171+
}
172+
} else {
173+
null
167174
}
168175
connectionToCustomizationUiItems[it.id] = customizationUiItems
169176

0 commit comments

Comments
 (0)