Skip to content

Commit afc5bf5

Browse files
committed
Merge remote-tracking branch 'origin/main' into feature/q-lsp-chat
Conflicts: plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/credentials/CodeWhispererClientAdaptor.kt plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/util/CodeWhispererUtil.kt plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererCodeCoverageTrackerTest.kt plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererUtilTest.kt plugins/amazonq/codewhisperer/jetbrains-community/tstFixtures/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt
2 parents 599df15 + 2720828 commit afc5bf5

File tree

8 files changed

+247
-1
lines changed

8 files changed

+247
-1
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
@@ -20,6 +20,7 @@ import com.intellij.openapi.editor.Editor
2020
import com.intellij.openapi.fileEditor.FileEditorManager
2121
import com.intellij.openapi.options.ShowSettingsUtil
2222
import kotlinx.coroutines.cancelChildren
23+
import kotlinx.coroutines.delay
2324
import kotlinx.coroutines.flow.catch
2425
import kotlinx.coroutines.flow.filter
2526
import kotlinx.coroutines.flow.first
@@ -35,6 +36,7 @@ import software.aws.toolkits.core.utils.getLogger
3536
import software.aws.toolkits.core.utils.info
3637
import software.aws.toolkits.core.utils.warn
3738
import software.aws.toolkits.jetbrains.core.coroutines.EDT
39+
import software.aws.toolkits.jetbrains.core.credentials.sono.isInternalUser
3840
import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
3941
import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthController
4042
import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthNeededState
@@ -45,6 +47,8 @@ import software.aws.toolkits.jetbrains.services.amazonq.onboarding.OnboardingPag
4547
import software.aws.toolkits.jetbrains.services.amazonq.project.ProjectContextController
4648
import software.aws.toolkits.jetbrains.services.amazonq.project.RelevantDocument
4749
import software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhispererConfigurable
50+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.getDiagnosticDifferences
51+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.getDocumentDiagnostics
4852
import software.aws.toolkits.jetbrains.services.cwc.InboundAppMessagesHandler
4953
import software.aws.toolkits.jetbrains.services.cwc.clients.chat.exceptions.ChatApiException
5054
import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatRequestData
@@ -207,6 +211,7 @@ class ChatController private constructor(
207211
val caret: Caret = editor.caretModel.primaryCaret
208212
val offset: Int = caret.offset
209213

214+
val oldDiagnostics = getDocumentDiagnostics(editor.document, context.project)
210215
ApplicationManager.getApplication().runWriteAction {
211216
WriteCommandAction.runWriteCommandAction(context.project) {
212217
if (caret.hasSelection()) {
@@ -218,6 +223,12 @@ class ChatController private constructor(
218223
ReferenceLogController.addReferenceLog(message.code, message.codeReference, editor, context.project, null)
219224
}
220225
}
226+
if (isInternalUser(getStartUrl(context.project))) {
227+
// wait for the IDE itself to update its diagnostics for current file
228+
delay(500)
229+
val newDiagnostics = getDocumentDiagnostics(editor.document, context.project)
230+
message.diagnosticsDifferences = getDiagnosticDifferences(oldDiagnostics, newDiagnostics)
231+
}
221232
}
222233
telemetryHelper.recordInteractWithMessage(message)
223234
}

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/service/CodeWhispererService.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import org.eclipse.lsp4j.TextDocumentIdentifier
3030
import org.eclipse.lsp4j.jsonrpc.JsonRpcException
3131
import org.eclipse.lsp4j.jsonrpc.messages.Either
3232
import software.amazon.awssdk.core.exception.SdkServiceException
33+
import software.amazon.awssdk.services.codewhispererruntime.model.IdeDiagnostic
3334
import software.amazon.awssdk.services.codewhispererruntime.model.ThrottlingException
3435
import software.aws.toolkits.core.utils.debug
3536
import software.aws.toolkits.core.utils.getLogger
@@ -71,6 +72,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhisperer
7172
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.notifyErrorCodeWhispererUsageLimit
7273
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.promptReAuth
7374
import software.aws.toolkits.jetbrains.services.codewhisperer.util.FileContextProvider
75+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.getDocumentDiagnostics
7476
import software.aws.toolkits.jetbrains.utils.isInjectedText
7577
import software.aws.toolkits.jetbrains.utils.isQExpired
7678
import software.aws.toolkits.resources.message
@@ -469,6 +471,7 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable {
469471
} catch (e: Exception) {
470472
LOG.warn { "Cannot get workspaceId from LSP'$e'" }
471473
}
474+
val diagnostics = getDocumentDiagnostics(editor.document, project)
472475
return RequestContext(
473476
project,
474477
editor,
@@ -479,6 +482,7 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable {
479482
latencyContext,
480483
customizationArn,
481484
workspaceId,
485+
diagnostics
482486
)
483487
}
484488

@@ -631,6 +635,7 @@ data class RequestContext(
631635
val latencyContext: LatencyContext,
632636
val customizationArn: String?,
633637
val workspaceId: String?,
638+
val diagnostics: List<IdeDiagnostic>?,
634639
)
635640

636641
data class ResponseContext(

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
@@ -21,7 +25,10 @@ import kotlinx.coroutines.Job
2125
import kotlinx.coroutines.delay
2226
import kotlinx.coroutines.launch
2327
import kotlinx.coroutines.yield
28+
import software.amazon.awssdk.services.codewhispererruntime.model.IdeDiagnostic
2429
import software.amazon.awssdk.services.codewhispererruntime.model.OptOutPreference
30+
import software.amazon.awssdk.services.codewhispererruntime.model.Position
31+
import software.amazon.awssdk.services.codewhispererruntime.model.Range
2532
import software.aws.toolkits.core.utils.getLogger
2633
import software.aws.toolkits.core.utils.warn
2734
import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
@@ -347,3 +354,88 @@ object CodeWhispererUtil {
347354
enum class CaretMovement {
348355
NO_CHANGE, MOVE_FORWARD, MOVE_BACKWARD
349356
}
357+
358+
fun getDiagnosticsType(message: String): String {
359+
val lowercaseMessage = message.lowercase()
360+
361+
val diagnosticPatterns = mapOf(
362+
"TYPE_ERROR" to listOf("type", "cast"),
363+
"SYNTAX_ERROR" to listOf("expected", "indent", "syntax"),
364+
"REFERENCE_ERROR" to listOf("undefined", "not defined", "undeclared", "reference", "symbol"),
365+
"BEST_PRACTICE" to listOf("deprecated", "unused", "uninitialized", "not initialized"),
366+
"SECURITY" to listOf("security", "vulnerability")
367+
)
368+
369+
return diagnosticPatterns
370+
.entries
371+
.firstOrNull { (_, keywords) ->
372+
keywords.any { lowercaseMessage.contains(it) }
373+
}
374+
?.key ?: "OTHER"
375+
}
376+
377+
fun convertSeverity(severity: HighlightSeverity): String = when {
378+
severity == HighlightSeverity.ERROR -> "ERROR"
379+
severity == HighlightSeverity.WARNING ||
380+
severity == HighlightSeverity.WEAK_WARNING -> "WARNING"
381+
severity == HighlightSeverity.INFORMATION -> "INFORMATION"
382+
severity.toString().contains("TEXT", ignoreCase = true) -> "HINT"
383+
severity == HighlightSeverity.INFO -> "INFORMATION"
384+
// For severities that might indicate performance issues
385+
severity.toString().contains("PERFORMANCE", ignoreCase = true) -> "WARNING"
386+
// For deprecation warnings
387+
severity.toString().contains("DEPRECATED", ignoreCase = true) -> "WARNING"
388+
// Default case
389+
else -> "INFORMATION"
390+
}
391+
392+
fun getDocumentDiagnostics(document: Document, project: Project): List<IdeDiagnostic> = runCatching {
393+
DocumentMarkupModel.forDocument(document, project, true)
394+
.allHighlighters
395+
.mapNotNull { it.errorStripeTooltip as? HighlightInfo }
396+
.filter { !it.description.isNullOrEmpty() }
397+
.map { info ->
398+
val startLine = document.getLineNumber(info.startOffset)
399+
val endLine = document.getLineNumber(info.endOffset)
400+
401+
IdeDiagnostic.builder()
402+
.ideDiagnosticType(getDiagnosticsType(info.description))
403+
.severity(convertSeverity(info.severity))
404+
.source(info.inspectionToolId)
405+
.range(
406+
Range.builder()
407+
.start(
408+
Position.builder()
409+
.line(startLine)
410+
.character(document.getLineStartOffset(startLine))
411+
.build()
412+
)
413+
.end(
414+
Position.builder()
415+
.line(endLine)
416+
.character(document.getLineStartOffset(endLine))
417+
.build()
418+
)
419+
.build()
420+
)
421+
.build()
422+
}
423+
}.getOrElse { e ->
424+
getLogger<CodeWhispererUtil>().warn { "Failed to get document diagnostics ${e.message}" }
425+
emptyList()
426+
}
427+
428+
data class DiagnosticDifferences(
429+
val added: List<IdeDiagnostic>,
430+
val removed: List<IdeDiagnostic>,
431+
)
432+
433+
fun serializeDiagnostics(diagnostic: IdeDiagnostic): String = "${diagnostic.source()}-${diagnostic.severity()}-${diagnostic.ideDiagnosticType()}"
434+
435+
fun getDiagnosticDifferences(oldDiagnostic: List<IdeDiagnostic>, newDiagnostic: List<IdeDiagnostic>): DiagnosticDifferences {
436+
val oldSet = oldDiagnostic.map { i -> serializeDiagnostics(i) }.toSet()
437+
val newSet = newDiagnostic.map { i -> serializeDiagnostics(i) }.toSet()
438+
val added = newDiagnostic.filter { i -> !oldSet.contains(serializeDiagnostics(i)) }.distinctBy { serializeDiagnostics(it) }
439+
val removed = oldDiagnostic.filter { i -> !newSet.contains(serializeDiagnostics(i)) }.distinctBy { serializeDiagnostics(it) }
440+
return DiagnosticDifferences(added, removed)
441+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ fun aRequestContext(
163163
),
164164
customizationArn = null,
165165
workspaceId = null,
166+
diagnostics = emptyList(),
166167
)
167168
}
168169

0 commit comments

Comments
 (0)