Skip to content

Commit 02db760

Browse files
Merge main into feature/remote-chat
2 parents 9521ad6 + 2720828 commit 02db760

File tree

11 files changed

+267
-2
lines changed

11 files changed

+267
-2
lines changed

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.intellij.openapi.fileEditor.FileEditorManager
2121
import com.intellij.openapi.options.ShowSettingsUtil
2222
import com.intellij.psi.PsiDocumentManager
2323
import kotlinx.coroutines.cancelChildren
24+
import kotlinx.coroutines.delay
2425
import kotlinx.coroutines.flow.catch
2526
import kotlinx.coroutines.flow.filter
2627
import kotlinx.coroutines.flow.first
@@ -36,6 +37,7 @@ import software.aws.toolkits.core.utils.getLogger
3637
import software.aws.toolkits.core.utils.info
3738
import software.aws.toolkits.core.utils.warn
3839
import software.aws.toolkits.jetbrains.core.coroutines.EDT
40+
import software.aws.toolkits.jetbrains.core.credentials.sono.isInternalUser
3941
import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
4042
import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthController
4143
import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthNeededState
@@ -49,6 +51,8 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhisp
4951
import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererUserModificationTracker
5052
import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent
5153
import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent
54+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.getDiagnosticDifferences
55+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.getDocumentDiagnostics
5256
import software.aws.toolkits.jetbrains.services.cwc.InboundAppMessagesHandler
5357
import software.aws.toolkits.jetbrains.services.cwc.clients.chat.exceptions.ChatApiException
5458
import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatRequestData
@@ -214,6 +218,7 @@ class ChatController private constructor(
214218
val caret: Caret = editor.caretModel.primaryCaret
215219
val offset: Int = caret.offset
216220

221+
val oldDiagnostics = getDocumentDiagnostics(editor.document, context.project)
217222
ApplicationManager.getApplication().runWriteAction {
218223
WriteCommandAction.runWriteCommandAction(context.project) {
219224
if (caret.hasSelection()) {
@@ -236,6 +241,12 @@ class ChatController private constructor(
236241
)
237242
}
238243
}
244+
if (isInternalUser(getStartUrl(context.project))) {
245+
// wait for the IDE itself to update its diagnostics for current file
246+
delay(500)
247+
val newDiagnostics = getDocumentDiagnostics(editor.document, context.project)
248+
message.diagnosticsDifferences = getDiagnosticDifferences(oldDiagnostics, newDiagnostics)
249+
}
239250
}
240251
telemetryHelper.recordInteractWithMessage(message)
241252

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/telemetry/TelemetryHelper.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ class TelemetryHelper(private val project: Project, private val sessionStorage:
270270
acceptedCharacterCount(message.code.length)
271271
acceptedLineCount(message.code.lines().size)
272272
hasProjectLevelContext(getMessageHasProjectContext(message.messageId))
273+
addedIdeDiagnostics(message.diagnosticsDifferences?.added)
274+
removedIdeDiagnostics(message.diagnosticsDifferences?.removed)
273275
}.build()
274276
}
275277

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/messages/CwcMessage.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthFollowUpType
1818
import software.aws.toolkits.jetbrains.services.amazonq.messages.AmazonQMessage
1919
import software.aws.toolkits.jetbrains.services.amazonq.onboarding.OnboardingPageInteractionType
2020
import software.aws.toolkits.jetbrains.services.amazonq.util.HighlightCommand
21+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.DiagnosticDifferences
2122
import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.FollowUpType
2223
import java.time.Instant
2324

@@ -95,6 +96,7 @@ sealed interface IncomingCwcMessage : CwcMessage {
9596
val codeBlockIndex: Int?,
9697
val totalCodeBlocks: Int?,
9798
val codeBlockLanguage: String?,
99+
var diagnosticsDifferences: DiagnosticDifferences?,
98100
) : IncomingCwcMessage, TabId, MessageId
99101

100102
data class TriggerTabIdReceived(

plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/TelemetryHelperTest.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitConte
4444
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor
4545
import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererCustomization
4646
import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererModelConfigurator
47+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.DiagnosticDifferences
4748
import software.aws.toolkits.jetbrains.services.cwc.clients.chat.ChatSession
4849
import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatRequestData
4950
import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.CodeNamesImpl
@@ -475,6 +476,7 @@ class TelemetryHelperTest {
475476
val inserTionTargetType = "insertionTargetType"
476477
val eventId = "eventId"
477478
val code = "println()"
479+
val diagnosticDifferences = DiagnosticDifferences(emptyList(), emptyList())
478480

479481
sut.recordInteractWithMessage(
480482
IncomingCwcMessage.InsertCodeAtCursorPosition(
@@ -487,7 +489,8 @@ class TelemetryHelperTest {
487489
eventId,
488490
codeBlockIndex,
489491
totalCodeBlocks,
490-
lang
492+
lang,
493+
diagnosticDifferences
491494
)
492495
)
493496

@@ -503,6 +506,8 @@ class TelemetryHelperTest {
503506
acceptedLineCount(code.lines().size)
504507
customizationArn(customizationArn)
505508
hasProjectLevelContext(false)
509+
addedIdeDiagnostics(emptyList())
510+
removedIdeDiagnostics(emptyList())
506511
}.build()
507512
)
508513
)

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import software.amazon.awssdk.services.codewhispererruntime.model.TargetCode
3737
import software.amazon.awssdk.services.codewhispererruntime.model.UserIntent
3838
import software.aws.toolkits.core.utils.debug
3939
import software.aws.toolkits.core.utils.getLogger
40+
import software.aws.toolkits.jetbrains.core.credentials.sono.isInternalUser
4041
import software.aws.toolkits.jetbrains.services.amazonq.codeWhispererUserContext
4142
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager
4243
import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererCustomization
@@ -47,6 +48,10 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.service.RequestCon
4748
import software.aws.toolkits.jetbrains.services.codewhisperer.service.ResponseContext
4849
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
4950
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.getTelemetryOptOutPreference
51+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.DiagnosticDifferences
52+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.getDiagnosticDifferences
53+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.getDocumentDiagnostics
54+
import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl
5055
import software.aws.toolkits.telemetry.CodewhispererCompletionType
5156
import software.aws.toolkits.telemetry.CodewhispererSuggestionState
5257
import java.time.Instant
@@ -340,7 +345,17 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW
340345
) {
341346
e2eLatency = 0.0
342347
}
343-
348+
var diffDiagnostics = DiagnosticDifferences(
349+
added = emptyList(),
350+
removed = emptyList()
351+
)
352+
if (suggestionState == CodewhispererSuggestionState.Accept && isInternalUser(getStartUrl(project))) {
353+
val oldDiagnostics = requestContext.diagnostics.orEmpty()
354+
// wait for the IDE itself to update its diagnostics for current file
355+
Thread.sleep(500)
356+
val newDiagnostics = getDocumentDiagnostics(requestContext.editor.document, project)
357+
diffDiagnostics = getDiagnosticDifferences(oldDiagnostics, newDiagnostics)
358+
}
344359
return bearerClient().sendTelemetryEvent { requestBuilder ->
345360
requestBuilder.telemetryEvent { telemetryEventBuilder ->
346361
telemetryEventBuilder.userTriggerDecisionEvent {
@@ -358,6 +373,8 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW
358373
it.customizationArn(requestContext.customizationArn.nullize(nullizeSpaces = true))
359374
it.numberOfRecommendations(numberOfRecommendations)
360375
it.acceptedCharacterCount(acceptedCharCount)
376+
it.addedIdeDiagnostics(diffDiagnostics.added)
377+
it.removedIdeDiagnostics(diffDiagnostics.removed)
361378
}
362379
}
363380
requestBuilder.optOutPreference(getTelemetryOptOutPreference())

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import software.amazon.awssdk.services.codewhispererruntime.model.Completion
3636
import software.amazon.awssdk.services.codewhispererruntime.model.FileContext
3737
import software.amazon.awssdk.services.codewhispererruntime.model.GenerateCompletionsRequest
3838
import software.amazon.awssdk.services.codewhispererruntime.model.GenerateCompletionsResponse
39+
import software.amazon.awssdk.services.codewhispererruntime.model.IdeDiagnostic
3940
import software.amazon.awssdk.services.codewhispererruntime.model.ProgrammingLanguage
4041
import software.amazon.awssdk.services.codewhispererruntime.model.RecommendationsWithReferencesPreference
4142
import software.amazon.awssdk.services.codewhispererruntime.model.ResourceNotFoundException
@@ -87,6 +88,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhisperer
8788
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.notifyErrorCodeWhispererUsageLimit
8889
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.promptReAuth
8990
import software.aws.toolkits.jetbrains.services.codewhisperer.util.FileContextProvider
91+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.getDocumentDiagnostics
9092
import software.aws.toolkits.jetbrains.settings.CodeWhispererSettings
9193
import software.aws.toolkits.jetbrains.utils.isInjectedText
9294
import software.aws.toolkits.jetbrains.utils.isQExpired
@@ -691,6 +693,7 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable {
691693
} catch (e: Exception) {
692694
LOG.warn { "Cannot get workspaceId from LSP'$e'" }
693695
}
696+
val diagnostics = getDocumentDiagnostics(editor.document, project)
694697
return RequestContext(
695698
project,
696699
editor,
@@ -703,6 +706,7 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable {
703706
customizationArn,
704707
profileArn,
705708
workspaceId,
709+
diagnostics
706710
)
707711
}
708712

@@ -895,6 +899,7 @@ data class RequestContext(
895899
val customizationArn: String?,
896900
val profileArn: String?,
897901
val workspaceId: String?,
902+
val diagnostics: List<IdeDiagnostic>?,
898903
) {
899904
// TODO: should make the entire getRequestContext() suspend function instead of making supplemental context only
900905
var supplementalContext: SupplementalContextInfo? = null

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/util/CodeWhispererUtil.kt

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@
33

44
package software.aws.toolkits.jetbrains.services.codewhisperer.util
55

6+
import com.intellij.codeInsight.daemon.impl.HighlightInfo
67
import com.intellij.codeInsight.lookup.LookupManager
78
import com.intellij.ide.BrowserUtil
9+
import com.intellij.lang.annotation.HighlightSeverity
810
import com.intellij.notification.NotificationAction
911
import com.intellij.openapi.application.ApplicationManager
1012
import com.intellij.openapi.application.runInEdt
13+
import com.intellij.openapi.editor.Document
1114
import com.intellij.openapi.editor.Editor
15+
import com.intellij.openapi.editor.impl.DocumentMarkupModel
1216
import com.intellij.openapi.editor.impl.EditorImpl
1317
import com.intellij.openapi.project.Project
1418
import com.intellij.openapi.vfs.VfsUtil
@@ -22,7 +26,10 @@ import kotlinx.coroutines.delay
2226
import kotlinx.coroutines.launch
2327
import kotlinx.coroutines.yield
2428
import software.amazon.awssdk.services.codewhispererruntime.model.Completion
29+
import software.amazon.awssdk.services.codewhispererruntime.model.IdeDiagnostic
2530
import software.amazon.awssdk.services.codewhispererruntime.model.OptOutPreference
31+
import software.amazon.awssdk.services.codewhispererruntime.model.Position
32+
import software.amazon.awssdk.services.codewhispererruntime.model.Range
2633
import software.aws.toolkits.core.utils.getLogger
2734
import software.aws.toolkits.core.utils.warn
2835
import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
@@ -361,3 +368,88 @@ object CodeWhispererUtil {
361368
enum class CaretMovement {
362369
NO_CHANGE, MOVE_FORWARD, MOVE_BACKWARD
363370
}
371+
372+
fun getDiagnosticsType(message: String): String {
373+
val lowercaseMessage = message.lowercase()
374+
375+
val diagnosticPatterns = mapOf(
376+
"TYPE_ERROR" to listOf("type", "cast"),
377+
"SYNTAX_ERROR" to listOf("expected", "indent", "syntax"),
378+
"REFERENCE_ERROR" to listOf("undefined", "not defined", "undeclared", "reference", "symbol"),
379+
"BEST_PRACTICE" to listOf("deprecated", "unused", "uninitialized", "not initialized"),
380+
"SECURITY" to listOf("security", "vulnerability")
381+
)
382+
383+
return diagnosticPatterns
384+
.entries
385+
.firstOrNull { (_, keywords) ->
386+
keywords.any { lowercaseMessage.contains(it) }
387+
}
388+
?.key ?: "OTHER"
389+
}
390+
391+
fun convertSeverity(severity: HighlightSeverity): String = when {
392+
severity == HighlightSeverity.ERROR -> "ERROR"
393+
severity == HighlightSeverity.WARNING ||
394+
severity == HighlightSeverity.WEAK_WARNING -> "WARNING"
395+
severity == HighlightSeverity.INFORMATION -> "INFORMATION"
396+
severity.toString().contains("TEXT", ignoreCase = true) -> "HINT"
397+
severity == HighlightSeverity.INFO -> "INFORMATION"
398+
// For severities that might indicate performance issues
399+
severity.toString().contains("PERFORMANCE", ignoreCase = true) -> "WARNING"
400+
// For deprecation warnings
401+
severity.toString().contains("DEPRECATED", ignoreCase = true) -> "WARNING"
402+
// Default case
403+
else -> "INFORMATION"
404+
}
405+
406+
fun getDocumentDiagnostics(document: Document, project: Project): List<IdeDiagnostic> = runCatching {
407+
DocumentMarkupModel.forDocument(document, project, true)
408+
.allHighlighters
409+
.mapNotNull { it.errorStripeTooltip as? HighlightInfo }
410+
.filter { !it.description.isNullOrEmpty() }
411+
.map { info ->
412+
val startLine = document.getLineNumber(info.startOffset)
413+
val endLine = document.getLineNumber(info.endOffset)
414+
415+
IdeDiagnostic.builder()
416+
.ideDiagnosticType(getDiagnosticsType(info.description))
417+
.severity(convertSeverity(info.severity))
418+
.source(info.inspectionToolId)
419+
.range(
420+
Range.builder()
421+
.start(
422+
Position.builder()
423+
.line(startLine)
424+
.character(document.getLineStartOffset(startLine))
425+
.build()
426+
)
427+
.end(
428+
Position.builder()
429+
.line(endLine)
430+
.character(document.getLineStartOffset(endLine))
431+
.build()
432+
)
433+
.build()
434+
)
435+
.build()
436+
}
437+
}.getOrElse { e ->
438+
getLogger<CodeWhispererUtil>().warn { "Failed to get document diagnostics ${e.message}" }
439+
emptyList()
440+
}
441+
442+
data class DiagnosticDifferences(
443+
val added: List<IdeDiagnostic>,
444+
val removed: List<IdeDiagnostic>,
445+
)
446+
447+
fun serializeDiagnostics(diagnostic: IdeDiagnostic): String = "${diagnostic.source()}-${diagnostic.severity()}-${diagnostic.ideDiagnosticType()}"
448+
449+
fun getDiagnosticDifferences(oldDiagnostic: List<IdeDiagnostic>, newDiagnostic: List<IdeDiagnostic>): DiagnosticDifferences {
450+
val oldSet = oldDiagnostic.map { i -> serializeDiagnostics(i) }.toSet()
451+
val newSet = newDiagnostic.map { i -> serializeDiagnostics(i) }.toSet()
452+
val added = newDiagnostic.filter { i -> !oldSet.contains(serializeDiagnostics(i)) }.distinctBy { serializeDiagnostics(it) }
453+
val removed = oldDiagnostic.filter { i -> !newSet.contains(serializeDiagnostics(i)) }.distinctBy { serializeDiagnostics(it) }
454+
return DiagnosticDifferences(added, removed)
455+
}

plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererCodeCoverageTrackerTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ internal class CodeWhispererCodeCoverageTrackerTestPython : CodeWhispererCodeCov
179179
aString(),
180180
aString(),
181181
aString(),
182+
emptyList()
182183
)
183184
val responseContext = ResponseContext("sessionId")
184185
val recommendationContext = RecommendationContext(

plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ class CodeWhispererServiceTest {
215215
customizationArn = "fake-arn",
216216
profileArn = "fake-arn",
217217
workspaceId = null,
218+
diagnostics = emptyList()
218219
)
219220
)
220221

0 commit comments

Comments
 (0)