From 6b0c4573e9674f1286abbe9ffded6830892381ac Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Wed, 11 Dec 2024 10:31:27 -0800 Subject: [PATCH 01/21] reduce frequency of system information polling --- .../bugfix-fef8426e-f36c-425f-af0e-a9811e01e2f5.json | 4 ++++ .../services/amazonq/project/manifest/ManifestManager.kt | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .changes/next-release/bugfix-fef8426e-f36c-425f-af0e-a9811e01e2f5.json diff --git a/.changes/next-release/bugfix-fef8426e-f36c-425f-af0e-a9811e01e2f5.json b/.changes/next-release/bugfix-fef8426e-f36c-425f-af0e-a9811e01e2f5.json new file mode 100644 index 0000000000..6723f2623b --- /dev/null +++ b/.changes/next-release/bugfix-fef8426e-f36c-425f-af0e-a9811e01e2f5.json @@ -0,0 +1,4 @@ +{ + "type" : "bugfix", + "description" : "Reduce frequency of system information query" +} \ No newline at end of file diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/manifest/ManifestManager.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/manifest/ManifestManager.kt index ba6e274e8b..4693db1004 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/manifest/ManifestManager.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/manifest/ManifestManager.kt @@ -15,7 +15,7 @@ import software.aws.toolkits.jetbrains.core.getTextFromUrl class ManifestManager { private val cloudFrontUrl = "https://aws-toolkit-language-servers.amazonaws.com/q-context/manifest.json" - val currentVersion = "0.1.27" + val currentVersion = "0.1.32" val currentOs = getOs() private val arch = CpuArch.CURRENT private val mapper = jacksonObjectMapper() From 47fadd324487de09aba7564c67e100d3b72e4c91 Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Thu, 19 Dec 2024 10:13:43 -0800 Subject: [PATCH 02/21] init commit of ai code gen % --- .../telemetry/UserWrittenCodeTracker.kt | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt new file mode 100644 index 0000000000..d3e4b3d242 --- /dev/null +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt @@ -0,0 +1,149 @@ +// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.codewhisperer.telemetry + +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.runReadAction +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.service +import com.intellij.openapi.editor.event.DocumentEvent +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import com.intellij.openapi.util.TextRange +import com.intellij.util.Alarm +import com.intellij.util.AlarmFactory +import info.debatty.java.stringsimilarity.Levenshtein +import org.assertj.core.util.VisibleForTesting +import software.amazon.awssdk.services.codewhispererruntime.model.CodeWhispererRuntimeException +import software.aws.toolkits.core.utils.debug +import software.aws.toolkits.core.utils.getLogger +import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor +import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererModelConfigurator +import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererLanguageManager +import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererUnknownLanguage +import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmingLanguage +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererCodeCoverageTracker.Companion +import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.getConnectionStartUrl +import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.getUnmodifiedAcceptedCharsCount +import software.aws.toolkits.jetbrains.services.codewhisperer.util.runIfIdcConnectionOrTelemetryEnabled +import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.InsertedCodeModificationEntry +import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl +import software.aws.toolkits.jetbrains.settings.AwsSettings +import software.aws.toolkits.jetbrains.settings.CodeWhispererSettings +import software.aws.toolkits.telemetry.AmazonqTelemetry +import software.aws.toolkits.telemetry.CodewhispererLanguage +import software.aws.toolkits.telemetry.CodewhispererTelemetry +import java.time.Duration +import java.time.Instant +import java.util.concurrent.LinkedBlockingDeque +import java.util.concurrent.atomic.AtomicBoolean + + +@Service(Service.Level.PROJECT) +class UserWrittenCodeTracker(private val project: Project) : Disposable { + private val userWrittenCodePerLanguage = mutableMapOf() + private val alarm = AlarmFactory.getInstance().create(Alarm.ThreadToUse.POOLED_THREAD, this) + + private val isShuttingDown = AtomicBoolean(false) + + init { + scheduleTracker() + } + + private fun scheduleTracker() { + if (!alarm.isDisposed && !isShuttingDown.get()) { + alarm.addRequest({ flush() }, DEFAULT_CHECK_INTERVAL.toMillis()) + } + } + + private fun isTelemetryEnabled(): Boolean = AwsSettings.getInstance().isTelemetryEnabled + + + private fun flush() { + try { + if (!isTelemetryEnabled()) { + return + } + for ((language, userWrittenCode) in userWrittenCodePerLanguage) { + + } + + } finally { + scheduleTracker() + } + } + + internal fun documentChanged(event: DocumentEvent) { + // When open a file for the first time, IDE will also emit DocumentEvent for loading with `isWholeTextReplaced = true` + // Added this condition to filter out those events + if (event.isWholeTextReplaced) { + LOG.debug { "event with isWholeTextReplaced flag: $event" } + if (event.oldTimeStamp == 0L) return + } + // only count total tokens when it is a user keystroke input + // do not count doc changes from copy & paste of >=50 characters + // do not count other changes from formatter, git command, etc + // edge case: event can be from user hit enter with indentation where change is \n\t\t, count as 1 char increase in total chars + // when event is auto closing [{(', there will be 2 separated events, both count as 1 char increase in total chars + val text = event.newFragment.toString() + if (event.newLength < DOCUMENT_COPY_THRESSHOLD && text.trim().isNotEmpty()) { + // count doc changes from <50 multi character input as total user written code + // ignore all white space changes, this usually comes from IntelliJ formatting + val language = "" + val newUserWrittenCode = userWrittenCodePerLanguage.getOrDefault(language, 0) + event.newLength + userWrittenCodePerLanguage.set(language, newUserWrittenCode) + } + } + + private fun sendUserModificationTelemetryToServiceAPI( + suggestion: AcceptedSuggestionEntry, + ) { + runIfIdcConnectionOrTelemetryEnabled(project) { + try { + // should be impossible from the caller logic + if (suggestion.vFile == null) return@runIfIdcConnectionOrTelemetryEnabled + val document = runReadAction { + FileDocumentManager.getInstance().getDocument(suggestion.vFile) + } + val modifiedSuggestion = document?.getText( + TextRange(suggestion.range.startOffset, suggestion.range.endOffset) + ).orEmpty() + val response = CodeWhispererClientAdaptor.getInstance(project) + .sendUserModificationTelemetry( + suggestion.sessionId, + suggestion.requestId, + CodeWhispererLanguageManager.getInstance().getLanguage(suggestion.vFile), + CodeWhispererModelConfigurator.getInstance().activeCustomization(project)?.arn.orEmpty(), + suggestion.suggestion.length, + getUnmodifiedAcceptedCharsCount(suggestion.suggestion, modifiedSuggestion) + ) + LOG.debug { "Successfully sent user modification telemetry. RequestId: ${response.responseMetadata().requestId()}" } + } catch (e: Exception) { + val requestId = if (e is CodeWhispererRuntimeException) e.requestId() else null + LOG.debug { + "Failed to send user modification telemetry. RequestId: $requestId, ErrorMessage: ${e.message}" + } + } + } + } + + companion object { + private val DEFAULT_CHECK_INTERVAL = Duration.ofMinutes(1) + private const val DEFAULT_MODIFICATION_INTERVAL_IN_SECONDS = 300 // 5 minutes + + private const val DOCUMENT_COPY_THRESSHOLD = 50 + private val LOG = getLogger() + + fun getInstance(project: Project) = project.service() + } + + override fun dispose() { + if (isShuttingDown.getAndSet(true)) { + return + } + + flush() + } +} From b0b5e3af42d8e1fca51c518233321e1ac83172f5 Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Thu, 19 Dec 2024 14:40:41 -0800 Subject: [PATCH 03/21] more code --- .../credentials/CodeWhispererClientAdaptor.kt | 6 + .../telemetry/UserWrittenCodeTracker.kt | 111 +++++++++--------- .../codewhispererruntime/service-2.json | 4 +- 3 files changed, 66 insertions(+), 55 deletions(-) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/credentials/CodeWhispererClientAdaptor.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/credentials/CodeWhispererClientAdaptor.kt index d1ecae07fa..9fdb8ae3bb 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/credentials/CodeWhispererClientAdaptor.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/credentials/CodeWhispererClientAdaptor.kt @@ -133,6 +133,8 @@ interface CodeWhispererClientAdaptor : Disposable { acceptedTokenCount: Long, totalTokenCount: Long, unmodifiedAcceptedTokenCount: Long?, + userWrittenCodeCharacterCount: Long?, + userWrittenCodeLineCount: Long?, ): SendTelemetryEventResponse fun sendUserModificationTelemetry( @@ -479,6 +481,8 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW acceptedTokenCount: Long, totalTokenCount: Long, unmodifiedAcceptedTokenCount: Long?, + userWrittenCodeCharacterCount: Long?, + userWrittenCodeLineCount: Long?, ): SendTelemetryEventResponse = bearerClient().sendTelemetryEvent { requestBuilder -> requestBuilder.telemetryEvent { telemetryEventBuilder -> telemetryEventBuilder.codeCoverageEvent { @@ -488,6 +492,8 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW it.totalCharacterCount(totalTokenCount.toInt()) it.timestamp(Instant.now()) it.unmodifiedAcceptedCharacterCount(unmodifiedAcceptedTokenCount?.toInt()) + it.userWrittenCodeCharacterCount(userWrittenCodeLineCount?.toInt()) + it.userWrittenCodeLineCount(userWrittenCodeLineCount?.toInt()) } } requestBuilder.optOutPreference(getTelemetryOptOutPreference()) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt index d3e4b3d242..f652195a2e 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt @@ -4,49 +4,36 @@ package software.aws.toolkits.jetbrains.services.codewhisperer.telemetry import com.intellij.openapi.Disposable -import com.intellij.openapi.application.runReadAction import com.intellij.openapi.components.Service import com.intellij.openapi.components.service import com.intellij.openapi.editor.event.DocumentEvent -import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Key -import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiDocumentManager import com.intellij.util.Alarm import com.intellij.util.AlarmFactory -import info.debatty.java.stringsimilarity.Levenshtein -import org.assertj.core.util.VisibleForTesting import software.amazon.awssdk.services.codewhispererruntime.model.CodeWhispererRuntimeException import software.aws.toolkits.core.utils.debug import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererModelConfigurator -import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererLanguageManager -import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererUnknownLanguage +import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmingLanguage -import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererCodeCoverageTracker.Companion -import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.getConnectionStartUrl -import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.getUnmodifiedAcceptedCharsCount import software.aws.toolkits.jetbrains.services.codewhisperer.util.runIfIdcConnectionOrTelemetryEnabled -import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.InsertedCodeModificationEntry -import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl import software.aws.toolkits.jetbrains.settings.AwsSettings -import software.aws.toolkits.jetbrains.settings.CodeWhispererSettings -import software.aws.toolkits.telemetry.AmazonqTelemetry -import software.aws.toolkits.telemetry.CodewhispererLanguage -import software.aws.toolkits.telemetry.CodewhispererTelemetry import java.time.Duration -import java.time.Instant -import java.util.concurrent.LinkedBlockingDeque import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger @Service(Service.Level.PROJECT) class UserWrittenCodeTracker(private val project: Project) : Disposable { - private val userWrittenCodePerLanguage = mutableMapOf() + private val userWrittenCodeLineCount = mutableMapOf() + private val userWrittenCodeCharacterCount = mutableMapOf() private val alarm = AlarmFactory.getInstance().create(Alarm.ThreadToUse.POOLED_THREAD, this) private val isShuttingDown = AtomicBoolean(false) + private val qInvocationCount: AtomicInteger = AtomicInteger(0) + private val isQMakingEdits = AtomicBoolean(false) init { scheduleTracker() @@ -60,22 +47,35 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { private fun isTelemetryEnabled(): Boolean = AwsSettings.getInstance().isTelemetryEnabled + fun onQFeatureInvoked() { + qInvocationCount.incrementAndGet() + } + + fun onQStartsMakingEdits() { + isQMakingEdits.set(true) + } + + fun onQFinishesMakingEdits() { + isQMakingEdits.set(false) + } private fun flush() { try { - if (!isTelemetryEnabled()) { + if (!isTelemetryEnabled() || qInvocationCount.get() <= 0) { return } - for ((language, userWrittenCode) in userWrittenCodePerLanguage) { - - } - + emitCodeWhispererCodeContribution() } finally { scheduleTracker() } } internal fun documentChanged(event: DocumentEvent) { + // do not listen to document changed made by Amazon Q itself + if (isQMakingEdits.get()) { + return + } + // When open a file for the first time, IDE will also emit DocumentEvent for loading with `isWholeTextReplaced = true` // Added this condition to filter out those events if (event.isWholeTextReplaced) { @@ -88,45 +88,48 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { // edge case: event can be from user hit enter with indentation where change is \n\t\t, count as 1 char increase in total chars // when event is auto closing [{(', there will be 2 separated events, both count as 1 char increase in total chars val text = event.newFragment.toString() + val lines = text.split('\n').size - 1 if (event.newLength < DOCUMENT_COPY_THRESSHOLD && text.trim().isNotEmpty()) { // count doc changes from <50 multi character input as total user written code // ignore all white space changes, this usually comes from IntelliJ formatting - val language = "" - val newUserWrittenCode = userWrittenCodePerLanguage.getOrDefault(language, 0) + event.newLength - userWrittenCodePerLanguage.set(language, newUserWrittenCode) + val language = PsiDocumentManager.getInstance(project).getPsiFile(event.document)?.programmingLanguage() + if (language != null) { + userWrittenCodeLineCount.set(language, userWrittenCodeLineCount.getOrDefault(language, 0) + lines) + userWrittenCodeCharacterCount.set(language, userWrittenCodeCharacterCount.getOrDefault(language, 0) + event.newLength) + } } } - private fun sendUserModificationTelemetryToServiceAPI( - suggestion: AcceptedSuggestionEntry, - ) { - runIfIdcConnectionOrTelemetryEnabled(project) { - try { - // should be impossible from the caller logic - if (suggestion.vFile == null) return@runIfIdcConnectionOrTelemetryEnabled - val document = runReadAction { - FileDocumentManager.getInstance().getDocument(suggestion.vFile) - } - val modifiedSuggestion = document?.getText( - TextRange(suggestion.range.startOffset, suggestion.range.endOffset) - ).orEmpty() - val response = CodeWhispererClientAdaptor.getInstance(project) - .sendUserModificationTelemetry( - suggestion.sessionId, - suggestion.requestId, - CodeWhispererLanguageManager.getInstance().getLanguage(suggestion.vFile), - CodeWhispererModelConfigurator.getInstance().activeCustomization(project)?.arn.orEmpty(), - suggestion.suggestion.length, - getUnmodifiedAcceptedCharsCount(suggestion.suggestion, modifiedSuggestion) + + internal fun emitCodeWhispererCodeContribution() { + val customizationArn: String? = CodeWhispererModelConfigurator.getInstance().activeCustomization(project)?.arn + for ((language, _) in userWrittenCodeCharacterCount) { + if (userWrittenCodeCharacterCount.getOrDefault(language, 0) <= 0 ) { + continue + } + runIfIdcConnectionOrTelemetryEnabled(project) { + // here acceptedTokensSize is the count of accepted chars post user modification + try { + val response = CodeWhispererClientAdaptor.getInstance(project).sendCodePercentageTelemetry( + language, + customizationArn, + 0, + 0, + 0, + userWrittenCodeCharacterCount = userWrittenCodeCharacterCount.get(language), + userWrittenCodeLineCount = userWrittenCodeLineCount.get((language)) ) - LOG.debug { "Successfully sent user modification telemetry. RequestId: ${response.responseMetadata().requestId()}" } - } catch (e: Exception) { - val requestId = if (e is CodeWhispererRuntimeException) e.requestId() else null - LOG.debug { - "Failed to send user modification telemetry. RequestId: $requestId, ErrorMessage: ${e.message}" + LOG.debug { "Successfully sent code percentage telemetry. RequestId: ${response.responseMetadata().requestId()}" } + } catch (e: Exception) { + val requestId = if (e is CodeWhispererRuntimeException) e.requestId() else null + LOG.debug { + "Failed to send code percentage telemetry. RequestId: $requestId, ErrorMessage: ${e.message}" + } } } } + + } companion object { diff --git a/plugins/core/sdk-codegen/codegen-resources/codewhispererruntime/service-2.json b/plugins/core/sdk-codegen/codegen-resources/codewhispererruntime/service-2.json index 00e2305eb2..bf28b26a6d 100644 --- a/plugins/core/sdk-codegen/codegen-resources/codewhispererruntime/service-2.json +++ b/plugins/core/sdk-codegen/codegen-resources/codewhispererruntime/service-2.json @@ -579,7 +579,9 @@ "timestamp": { "shape": "Timestamp" }, "unmodifiedAcceptedCharacterCount": { "shape": "PrimitiveInteger" }, "totalNewCodeCharacterCount": { "shape": "PrimitiveInteger" }, - "totalNewCodeLineCount": { "shape": "PrimitiveInteger" } + "totalNewCodeLineCount": { "shape": "PrimitiveInteger" }, + "userWrittenCodeCharacterCount": { "shape": "PrimitiveInteger" }, + "userWrittenCodeLineCount": { "shape": "PrimitiveInteger" } } }, "CodeFixAcceptanceEvent": { From c1f3716f9aaf86922c0197f2efe4006a1f7156de Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Fri, 20 Dec 2024 11:49:23 -0800 Subject: [PATCH 04/21] update --- .../editor/CodeWhispererEditorListener.kt | 5 +++ .../telemetry/UserWrittenCodeTracker.kt | 41 +++++++++++-------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorListener.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorListener.kt index 323e66dfa4..e8f2884497 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorListener.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorListener.kt @@ -15,6 +15,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmi import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatus import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatusNew import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererCodeCoverageTracker +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker class CodeWhispererEditorListener : EditorFactoryListener { override fun editorCreated(event: EditorFactoryEvent) { @@ -40,6 +41,10 @@ class CodeWhispererEditorListener : EditorFactoryListener { activateTrackerIfNotActive() documentChanged(event) } + UserWrittenCodeTracker.getInstance(project).apply { + activateTrackerIfNotActive() + documentChanged(event) + } } }, editor.disposable diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt index f652195a2e..5790c53883 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt @@ -34,19 +34,30 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { private val isShuttingDown = AtomicBoolean(false) private val qInvocationCount: AtomicInteger = AtomicInteger(0) private val isQMakingEdits = AtomicBoolean(false) + private val isActive: AtomicBoolean = AtomicBoolean(false) - init { + @Synchronized + fun activateTrackerIfNotActive() { + // tracker will only be activated if and only if IsTelemetryEnabled = true && isActive = false + if (!isTelemetryEnabled() || isActive.getAndSet(true)) return scheduleTracker() } + private fun reset() { + userWrittenCodeLineCount.clear() + userWrittenCodeCharacterCount.clear() + qInvocationCount.set(0) + isQMakingEdits.set(false) + isActive.set(false) + isShuttingDown.set(false) + } + private fun isTelemetryEnabled(): Boolean = AwsSettings.getInstance().isTelemetryEnabled + private fun scheduleTracker() { if (!alarm.isDisposed && !isShuttingDown.get()) { - alarm.addRequest({ flush() }, DEFAULT_CHECK_INTERVAL.toMillis()) + alarm.addRequest({ flush() }, Duration.ofSeconds(DEFAULT_MODIFICATION_INTERVAL_IN_SECONDS).toMillis()) } } - - private fun isTelemetryEnabled(): Boolean = AwsSettings.getInstance().isTelemetryEnabled - fun onQFeatureInvoked() { qInvocationCount.incrementAndGet() } @@ -66,6 +77,7 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { } emitCodeWhispererCodeContribution() } finally { + reset() scheduleTracker() } } @@ -89,19 +101,19 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { // when event is auto closing [{(', there will be 2 separated events, both count as 1 char increase in total chars val text = event.newFragment.toString() val lines = text.split('\n').size - 1 - if (event.newLength < DOCUMENT_COPY_THRESSHOLD && text.trim().isNotEmpty()) { + if (event.newLength < COPY_THRESHOLD && text.trim().isNotEmpty()) { // count doc changes from <50 multi character input as total user written code // ignore all white space changes, this usually comes from IntelliJ formatting val language = PsiDocumentManager.getInstance(project).getPsiFile(event.document)?.programmingLanguage() if (language != null) { - userWrittenCodeLineCount.set(language, userWrittenCodeLineCount.getOrDefault(language, 0) + lines) - userWrittenCodeCharacterCount.set(language, userWrittenCodeCharacterCount.getOrDefault(language, 0) + event.newLength) + userWrittenCodeLineCount[language] = userWrittenCodeLineCount.getOrDefault(language, 0) + lines + userWrittenCodeCharacterCount[language] = userWrittenCodeCharacterCount.getOrDefault(language, 0) + event.newLength } } } - internal fun emitCodeWhispererCodeContribution() { + private fun emitCodeWhispererCodeContribution() { val customizationArn: String? = CodeWhispererModelConfigurator.getInstance().activeCustomization(project)?.arn for ((language, _) in userWrittenCodeCharacterCount) { if (userWrittenCodeCharacterCount.getOrDefault(language, 0) <= 0 ) { @@ -116,8 +128,8 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { 0, 0, 0, - userWrittenCodeCharacterCount = userWrittenCodeCharacterCount.get(language), - userWrittenCodeLineCount = userWrittenCodeLineCount.get((language)) + userWrittenCodeCharacterCount = userWrittenCodeCharacterCount[language], + userWrittenCodeLineCount = userWrittenCodeLineCount[(language)] ) LOG.debug { "Successfully sent code percentage telemetry. RequestId: ${response.responseMetadata().requestId()}" } } catch (e: Exception) { @@ -129,14 +141,12 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { } } - } companion object { - private val DEFAULT_CHECK_INTERVAL = Duration.ofMinutes(1) - private const val DEFAULT_MODIFICATION_INTERVAL_IN_SECONDS = 300 // 5 minutes + private const val DEFAULT_MODIFICATION_INTERVAL_IN_SECONDS = 300L // 5 minutes - private const val DOCUMENT_COPY_THRESSHOLD = 50 + private const val COPY_THRESHOLD = 50 private val LOG = getLogger() fun getInstance(project: Project) = project.service() @@ -146,7 +156,6 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { if (isShuttingDown.getAndSet(true)) { return } - flush() } } From 6f5a1caa63ce86cb1495460fa9f1e352aaf623ed Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Thu, 26 Dec 2024 12:23:37 -0800 Subject: [PATCH 05/21] use topic event --- .../services/cwc/controller/ChatController.kt | 14 +++++- .../chat/messenger/ChatPromptHandler.kt | 6 +++ .../cwc/inline/InlineChatController.kt | 5 +++ .../service/CodeWhispererService.kt | 4 ++ .../telemetry/UserWrittenCodeTracker.kt | 45 ++++++++++++++----- 5 files changed, 62 insertions(+), 12 deletions(-) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt index 4c034c3127..ad4d1ca267 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt @@ -49,6 +49,8 @@ import software.aws.toolkits.jetbrains.services.amazonq.project.ProjectContextCo import software.aws.toolkits.jetbrains.services.amazonq.project.RelevantDocument import software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhispererConfigurable import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererUserModificationTracker +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.cwc.InboundAppMessagesHandler import software.aws.toolkits.jetbrains.services.cwc.clients.chat.exceptions.ChatApiException import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatRequestData @@ -215,6 +217,9 @@ class ChatController private constructor( } override suspend fun processInsertCodeAtCursorPosition(message: IncomingCwcMessage.InsertCodeAtCursorPosition) { + + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.STARTS_EDITING) withContext(EDT) { val editor: Editor = FileEditorManager.getInstance(context.project).selectedTextEditor ?: return@withContext @@ -245,6 +250,9 @@ class ChatController private constructor( } } telemetryHelper.recordInteractWithMessage(message) + + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.FINISHES_EDITING) } override suspend fun processStopResponseMessage(message: IncomingCwcMessage.StopResponse) { @@ -438,7 +446,8 @@ class ChatController private constructor( sessionInfo.history.add(requestData) telemetryHelper.recordEnterFocusConversation(tabId) telemetryHelper.recordStartConversation(tabId, requestData) - + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.INVOCATION) // Send the request to the API and publish the responses back to the UI. // This is launched in a scope attached to the sessionInfo so that the Job can be cancelled on a per-session basis. ChatPromptHandler(telemetryHelper).handle(tabId, triggerId, requestData, sessionInfo, shouldAddIndexInProgressMessage) @@ -558,5 +567,8 @@ class ChatController private constructor( .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) .setSerializationInclusion(JsonInclude.Include.NON_NULL) + + } } + diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/messenger/ChatPromptHandler.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/messenger/ChatPromptHandler.kt index 0b6c01b805..b1abef3bff 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/messenger/ChatPromptHandler.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/messenger/ChatPromptHandler.kt @@ -3,6 +3,7 @@ package software.aws.toolkits.jetbrains.services.cwc.controller.chat.messenger +import com.intellij.openapi.application.ApplicationManager import kotlinx.coroutines.CancellationException import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow @@ -13,6 +14,8 @@ import software.amazon.awssdk.awscore.exception.AwsServiceException import software.amazon.awssdk.services.codewhispererstreaming.model.CodeWhispererStreamingException import software.aws.toolkits.core.utils.convertMarkdownToHTML import software.aws.toolkits.core.utils.extractCodeBlockLanguage +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.cwc.clients.chat.exceptions.ChatApiException import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatRequestData import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatResponseEvent @@ -115,6 +118,9 @@ class ChatPromptHandler(private val telemetryHelper: TelemetryHelper) { ) telemetryHelper.recordAddMessage(data, response, responseText.length, statusCode, countTotalNumberOfCodeBlocks(responseText)) emit(response) + + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.INVOCATION) } .catch { exception -> val statusCode = if (exception is AwsServiceException) exception.statusCode() else 0 diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt index bcadef007a..c523484049 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt @@ -58,6 +58,8 @@ import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthController import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AMAZON_Q_WINDOW_ID import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererModelConfigurator import software.aws.toolkits.jetbrains.services.codewhisperer.model.CaretPosition +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatRequestData import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.TriggerType import software.aws.toolkits.jetbrains.services.cwc.controller.ReferenceLogController @@ -711,6 +713,9 @@ class InlineChatController( canPopupAbort.set(true) undoChanges() } + + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.FINISHES_EDITING) return errorMessage } diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt index f11c501402..7a1dec74c7 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt @@ -74,6 +74,8 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.model.TriggerTypeI import software.aws.toolkits.jetbrains.services.codewhisperer.model.WorkerContext import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispererPopupManager import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.util.CaretMovement import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeInsightsSettingsFacade import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants @@ -261,6 +263,8 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable { lastRecommendationIndex += response.completions().size ApplicationManager.getApplication().messageBus.syncPublisher(CODEWHISPERER_CODE_COMPLETION_PERFORMED) .onSuccess(requestContext.fileContextInfo) + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.INVOCATION) CodeWhispererTelemetryService.getInstance().sendServiceInvocationEvent( requestId, requestContext, diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt index 5790c53883..3ca67ce788 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt @@ -4,6 +4,7 @@ package software.aws.toolkits.jetbrains.services.codewhisperer.telemetry import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.Service import com.intellij.openapi.components.service import com.intellij.openapi.editor.event.DocumentEvent @@ -11,6 +12,7 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiDocumentManager import com.intellij.util.Alarm import com.intellij.util.AlarmFactory +import com.intellij.util.messages.Topic import software.amazon.awssdk.services.codewhispererruntime.model.CodeWhispererRuntimeException import software.aws.toolkits.core.utils.debug import software.aws.toolkits.core.utils.getLogger @@ -40,6 +42,21 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { fun activateTrackerIfNotActive() { // tracker will only be activated if and only if IsTelemetryEnabled = true && isActive = false if (!isTelemetryEnabled() || isActive.getAndSet(true)) return + + // count q service invocations + val conn = ApplicationManager.getApplication().messageBus.connect() + conn.subscribe( + Q_FEATURE_TOPIC, + object : QFeatureListener { + override fun onEvent(event: QFeatureEvent) { + when(event) { + QFeatureEvent.INVOCATION -> qInvocationCount.getAndIncrement() + QFeatureEvent.STARTS_EDITING -> isQMakingEdits.set(true) + QFeatureEvent.FINISHES_EDITING -> isQMakingEdits.set(false) + } + } + } + ) scheduleTracker() } @@ -58,17 +75,6 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { alarm.addRequest({ flush() }, Duration.ofSeconds(DEFAULT_MODIFICATION_INTERVAL_IN_SECONDS).toMillis()) } } - fun onQFeatureInvoked() { - qInvocationCount.incrementAndGet() - } - - fun onQStartsMakingEdits() { - isQMakingEdits.set(true) - } - - fun onQFinishesMakingEdits() { - isQMakingEdits.set(false) - } private fun flush() { try { @@ -150,6 +156,12 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { private val LOG = getLogger() fun getInstance(project: Project) = project.service() + + val Q_FEATURE_TOPIC: Topic = Topic.create( + "Q service events", + QFeatureListener::class.java + ) + } override fun dispose() { @@ -159,3 +171,14 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { flush() } } + +enum class QFeatureEvent { + INVOCATION, + STARTS_EDITING, + FINISHES_EDITING +} + +interface QFeatureListener { + fun onEvent(event: QFeatureEvent) +} + From 5d44be97a315ec20d1cade5cc141598b3714e3fc Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Thu, 26 Dec 2024 15:04:41 -0800 Subject: [PATCH 06/21] listen to all q editor edits --- .../services/amazonqDoc/controller/DocController.kt | 7 +++++++ .../controller/FeatureDevController.kt | 7 +++++++ .../services/cwc/inline/InlineChatController.kt | 11 +++++++++-- .../codescan/utils/CodeWhispererCodeScanIssueUtils.kt | 6 ++++++ .../editor/CodeWhispererEditorManager.kt | 7 +++++++ .../editor/CodeWhispererEditorManagerNew.kt | 8 ++++++++ .../codewhisperer/telemetry/UserWrittenCodeTracker.kt | 1 + 7 files changed, 45 insertions(+), 2 deletions(-) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt index 8352d621d8..249286638e 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt @@ -10,6 +10,7 @@ import com.intellij.diff.contents.EmptyContent import com.intellij.diff.requests.SimpleDiffRequest import com.intellij.diff.util.DiffUserDataKeys import com.intellij.ide.BrowserUtil +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.runInEdt import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.editor.Caret @@ -74,6 +75,8 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.Delete import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.NewFileZipInfo import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.SessionStatePhase import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.CancellationTokenSource +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.FeedbackComment import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl import software.aws.toolkits.jetbrains.services.telemetry.TelemetryService @@ -398,6 +401,8 @@ class DocController( logger.debug { "$FEATURE_NAME: Processing InsertCodeAtCursorPosition: $message" } withContext(EDT) { + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.STARTS_EDITING) val editor: Editor = FileEditorManager.getInstance(context.project).selectedTextEditor ?: return@withContext val caret: Caret = editor.caretModel.primaryCaret @@ -409,6 +414,8 @@ class DocController( } editor.document.insertString(offset, message.code) } + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.FINISHES_EDITING) } } diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt index ddc3e2f496..659b46b0f1 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt @@ -11,6 +11,7 @@ import com.intellij.diff.editor.ChainDiffVirtualFile import com.intellij.diff.editor.DiffEditorTabFilesManager import com.intellij.diff.requests.SimpleDiffRequest import com.intellij.ide.BrowserUtil +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.runInEdt import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.editor.Caret @@ -71,6 +72,8 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.Sessio import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.storage.ChatSessionStorage import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.InsertAction import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.getFollowUpOptions +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.util.content import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.FeedbackComment import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl @@ -191,6 +194,8 @@ class FeatureDevController( logger.debug { "$FEATURE_NAME: Processing InsertCodeAtCursorPosition: $message" } withContext(EDT) { + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.STARTS_EDITING) val editor: Editor = FileEditorManager.getInstance(context.project).selectedTextEditor ?: return@withContext val caret: Caret = editor.caretModel.primaryCaret @@ -202,6 +207,8 @@ class FeatureDevController( } editor.document.insertString(offset, message.code) } + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.FINISHES_EDITING) } } diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt index c523484049..ccdd03ade0 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt @@ -193,7 +193,6 @@ class InlineChatController( private fun addPopupListeners(popup: JBPopup, editor: Editor) { val popupListener = object : JBPopupListener { - override fun onClosed(event: LightweightWindowEvent) { if (canPopupAbort.get() && event.asPopup().isDisposed) { popupCancelHandler.invoke(editor) @@ -536,6 +535,8 @@ class InlineChatController( private fun insertString(editor: Editor, offset: Int, text: String): RangeMarker { lateinit var rangeMarker: RangeMarker + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.STARTS_EDITING) ApplicationManager.getApplication().invokeAndWait { CommandProcessor.getInstance().runUndoTransparentAction { WriteCommandAction.runWriteCommandAction(project) { @@ -545,11 +546,15 @@ class InlineChatController( highlightCodeWithBackgroundColor(editor, rangeMarker.startOffset, rangeMarker.endOffset, true) } } - + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.FINISHES_EDITING) return rangeMarker } private fun replaceString(document: Document, start: Int, end: Int, text: String) { + + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.STARTS_EDITING) ApplicationManager.getApplication().invokeAndWait { CommandProcessor.getInstance().runUndoTransparentAction { WriteCommandAction.runWriteCommandAction(project) { @@ -557,6 +562,8 @@ class InlineChatController( } } } + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.FINISHES_EDITING) } private fun highlightString(editor: Editor, start: Int, end: Int, isInsert: Boolean) { diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/utils/CodeWhispererCodeScanIssueUtils.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/utils/CodeWhispererCodeScanIssueUtils.kt index f1d581673b..870040b8bc 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/utils/CodeWhispererCodeScanIssueUtils.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/utils/CodeWhispererCodeScanIssueUtils.kt @@ -38,6 +38,8 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.CodeWhisp import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmingLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.toolwindow.CodeWhispererCodeReferenceManager import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.CODE_SCAN_ISSUE_TITLE_MAX_LENGTH @@ -331,6 +333,8 @@ fun applySuggestedFix(project: Project, issue: CodeWhispererCodeScanIssue) { try { val manager = CodeWhispererCodeReferenceManager.getInstance(issue.project) WriteCommandAction.runWriteCommandAction(issue.project) { + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.STARTS_EDITING) val document = FileDocumentManager.getInstance().getDocument(issue.file) ?: return@runWriteCommandAction val documentContent = document.text @@ -343,6 +347,8 @@ fun applySuggestedFix(project: Project, issue: CodeWhispererCodeScanIssue) { LOG.debug { "Original content from reference span: $originalContent" } manager.addReferenceLogPanelEntry(reference = reference, null, null, originalContent.split("\n")) } + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.FINISHES_EDITING) } if (issue.suggestedFixes[0].references.isNotEmpty()) { manager.toolWindow?.show() diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt index 22c8c2176b..2f3808da1b 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt @@ -15,6 +15,8 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.model.InvocationCo import software.aws.toolkits.jetbrains.services.codewhisperer.model.SessionContext import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispererPopupManager import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.util.CaretMovement import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.PAIRED_BRACKETS import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.PAIRED_QUOTES @@ -44,9 +46,14 @@ class CodeWhispererEditorManager { val endOffsetToReplace = if (insertEndOffset != -1) insertEndOffset else primaryCaret.offset WriteCommandAction.runWriteCommandAction(project) { + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.STARTS_EDITING) document.replaceString(originalOffset, endOffsetToReplace, reformatted) PsiDocumentManager.getInstance(project).commitDocument(document) primaryCaret.moveToOffset(endOffset + detail.rightOverlap.length) + + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.FINISHES_EDITING) } ApplicationManager.getApplication().invokeLater { diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt index a244c31232..9a86dc4c83 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt @@ -16,6 +16,8 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispere import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispererPopupManagerNew import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererServiceNew import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryServiceNew +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.util.CaretMovement import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.PAIRED_BRACKETS import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.PAIRED_QUOTES @@ -51,9 +53,15 @@ class CodeWhispererEditorManagerNew { preview.detail.isAccepted = true WriteCommandAction.runWriteCommandAction(project) { + + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.STARTS_EDITING) document.replaceString(originalOffset, endOffsetToReplace, reformatted) PsiDocumentManager.getInstance(project).commitDocument(document) primaryCaret.moveToOffset(endOffset + detail.rightOverlap.length) + + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.FINISHES_EDITING) } ApplicationManager.getApplication().invokeLater { diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt index 3ca67ce788..88059f455f 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt @@ -68,6 +68,7 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { isActive.set(false) isShuttingDown.set(false) } + private fun isTelemetryEnabled(): Boolean = AwsSettings.getInstance().isTelemetryEnabled private fun scheduleTracker() { From 3cc36c5fc363bfe3c97d3815962f0b09928c6d1c Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Thu, 26 Dec 2024 15:11:59 -0800 Subject: [PATCH 07/21] add event whenever Q feature is used --- .../amazonqCodeScan/controller/CodeScanChatHelper.kt | 6 +++++- .../amazonqCodeTest/controller/CodeTestChatController.kt | 4 ++++ .../services/amazonqDoc/controller/DocController.kt | 3 ++- .../amazonqFeatureDev/controller/FeatureDevController.kt | 3 ++- .../controller/CodeTransformChatController.kt | 6 +++++- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeScan/controller/CodeScanChatHelper.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeScan/controller/CodeScanChatHelper.kt index 6a9acbbfc0..9c505afcb5 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeScan/controller/CodeScanChatHelper.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeScan/controller/CodeScanChatHelper.kt @@ -3,6 +3,7 @@ package software.aws.toolkits.jetbrains.services.amazonqCodeScan.controller +import com.intellij.openapi.application.ApplicationManager import software.aws.toolkits.jetbrains.services.amazonq.messages.MessagePublisher import software.aws.toolkits.jetbrains.services.amazonqCodeScan.buildClearPromptProgressMessage import software.aws.toolkits.jetbrains.services.amazonqCodeScan.buildPromptProgressMessage @@ -11,6 +12,8 @@ import software.aws.toolkits.jetbrains.services.amazonqCodeScan.messages.CodeSca import software.aws.toolkits.jetbrains.services.amazonqCodeScan.messages.CodeScanChatMessageContent import software.aws.toolkits.jetbrains.services.amazonqCodeScan.messages.UpdatePlaceholderMessage import software.aws.toolkits.jetbrains.services.amazonqCodeScan.storage.ChatSessionStorage +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.cwc.messages.ChatMessageType import java.util.UUID @@ -34,7 +37,8 @@ class CodeScanChatHelper( clearPreviousItemButtons: Boolean? = false, ) { if (isInValidSession()) return - + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.INVOCATION) messagePublisher.publish( CodeScanChatMessage( tabId = activeCodeScanTabId as String, diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt index eba7f1334c..f4d65109fb 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt @@ -70,6 +70,8 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendA import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmingLanguage +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.toolwindow.CodeWhispererCodeReferenceManager import software.aws.toolkits.jetbrains.services.cwc.ChatConstants import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatRequestData @@ -1181,6 +1183,8 @@ class CodeTestChatController( "Processing message: $message " + "tabId: $tabId" } + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.INVOCATION) when (session.conversationState) { ConversationState.WAITING_FOR_BUILD_COMMAND_INPUT -> handleBuildCommandInput(session, message) ConversationState.WAITING_FOR_REGENERATE_INPUT -> handleRegenerateInput(session, message) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt index 249286638e..bd938dcb58 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt @@ -830,7 +830,8 @@ class DocController( is PrepareDocGenerationState -> state.filePaths else -> emptyList() } - + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.INVOCATION) processOpenDiff( message = IncomingDocMessage.OpenDiff(tabId = tabId, filePath = filePaths[0].zipFilePath, deleted = false) ) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt index 659b46b0f1..2252265e63 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt @@ -686,7 +686,8 @@ class FeatureDevController( } session.preloader(message, messenger) - + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.INVOCATION) when (session.sessionState.phase) { SessionStatePhase.CODEGEN -> onCodeGeneration(session, message, tabId) else -> null diff --git a/plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/controller/CodeTransformChatController.kt b/plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/controller/CodeTransformChatController.kt index a67d4b0b68..27e52f49d5 100644 --- a/plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/controller/CodeTransformChatController.kt +++ b/plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/controller/CodeTransformChatController.kt @@ -4,6 +4,7 @@ package software.aws.toolkits.jetbrains.services.codemodernizer.controller import com.intellij.ide.BrowserUtil +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.runInEdt import com.intellij.openapi.fileChooser.FileChooser import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory @@ -110,6 +111,8 @@ import software.aws.toolkits.jetbrains.services.codemodernizer.utils.toVirtualFi import software.aws.toolkits.jetbrains.services.codemodernizer.utils.tryGetJdk import software.aws.toolkits.jetbrains.services.codemodernizer.utils.unzipFile import software.aws.toolkits.jetbrains.services.codemodernizer.utils.validateSctMetadata +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.cwc.messages.ChatMessageType import software.aws.toolkits.resources.message import software.aws.toolkits.telemetry.CodeTransformVCSViewerSrcComponents @@ -136,7 +139,8 @@ class CodeTransformChatController( if (objective == "language upgrade" || objective == "sql conversion") { telemetry.submitSelection(objective) } - + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(QFeatureEvent.INVOCATION) when (objective) { "language upgrade" -> this.handleLanguageUpgrade() "sql conversion" -> this.handleSQLConversion() From 04a7dceaaa0f0b7c5c8ef5c21ba6fe05aac735c8 Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Thu, 26 Dec 2024 17:36:33 -0800 Subject: [PATCH 08/21] fix build error --- .../telemetry/CodeWhispererCodeCoverageTracker.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/CodeWhispererCodeCoverageTracker.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/CodeWhispererCodeCoverageTracker.kt index dc512c1ca9..46bcc93574 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/CodeWhispererCodeCoverageTracker.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/CodeWhispererCodeCoverageTracker.kt @@ -253,7 +253,9 @@ abstract class CodeWhispererCodeCoverageTracker( customizationArn, acceptedCharsCount, totalCharsCount, - unmodifiedAcceptedCharsCount + unmodifiedAcceptedCharsCount, + 0, + 0 ) LOG.debug { "Successfully sent code percentage telemetry. RequestId: ${response.responseMetadata().requestId()}" } } catch (e: Exception) { From 8cd9edadef544b238719ac64289d658e0648b873 Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Fri, 27 Dec 2024 11:38:05 -0800 Subject: [PATCH 09/21] add unit test --- .../telemetry/UserWrittenCodeTracker.kt | 14 +- .../CodeWhispererClientAdaptorTest.kt | 2 +- .../UserWrittenCodeTrackerTest.kt | 192 ++++++++++++++++++ 3 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt index 88059f455f..7898318018 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt @@ -13,6 +13,7 @@ import com.intellij.psi.PsiDocumentManager import com.intellij.util.Alarm import com.intellij.util.AlarmFactory import com.intellij.util.messages.Topic +import org.jetbrains.annotations.TestOnly import software.amazon.awssdk.services.codewhispererruntime.model.CodeWhispererRuntimeException import software.aws.toolkits.core.utils.debug import software.aws.toolkits.core.utils.getLogger @@ -29,12 +30,12 @@ import java.util.concurrent.atomic.AtomicInteger @Service(Service.Level.PROJECT) class UserWrittenCodeTracker(private val project: Project) : Disposable { - private val userWrittenCodeLineCount = mutableMapOf() - private val userWrittenCodeCharacterCount = mutableMapOf() + val userWrittenCodeLineCount = mutableMapOf() + val userWrittenCodeCharacterCount = mutableMapOf() private val alarm = AlarmFactory.getInstance().create(Alarm.ThreadToUse.POOLED_THREAD, this) private val isShuttingDown = AtomicBoolean(false) - private val qInvocationCount: AtomicInteger = AtomicInteger(0) + val qInvocationCount: AtomicInteger = AtomicInteger(0) private val isQMakingEdits = AtomicBoolean(false) private val isActive: AtomicBoolean = AtomicBoolean(false) @@ -60,7 +61,7 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { scheduleTracker() } - private fun reset() { + fun reset() { userWrittenCodeLineCount.clear() userWrittenCodeCharacterCount.clear() qInvocationCount.set(0) @@ -171,6 +172,11 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { } flush() } + + @TestOnly + fun forceTrackerFlush() { + alarm.drainRequestsInTest() + } } enum class QFeatureEvent { diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererClientAdaptorTest.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererClientAdaptorTest.kt index 854d74469a..86ef92f8cc 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererClientAdaptorTest.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererClientAdaptorTest.kt @@ -391,7 +391,7 @@ class CodeWhispererClientAdaptorTest { @Test fun `sendTelemetryEvent for codePercentage respects telemetry optin status`() { sendTelemetryEventOptOutCheckHelper { - sut.sendCodePercentageTelemetry(aProgrammingLanguage(), aString(), 0, 1, 0) + sut.sendCodePercentageTelemetry(aProgrammingLanguage(), aString(), 0, 1, 0, 0, 0) } } diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt new file mode 100644 index 0000000000..e845897fc4 --- /dev/null +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt @@ -0,0 +1,192 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.codewhisperer + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.command.WriteCommandAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.event.DocumentEvent +import com.intellij.openapi.project.Project +import com.intellij.testFramework.DisposableRule +import com.intellij.testFramework.fixtures.CodeInsightTestFixture +import com.intellij.testFramework.replaceService +import com.intellij.testFramework.runInEdtAndWait +import org.assertj.core.api.Assertions.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.internal.verification.Times +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.verify +import software.aws.toolkits.core.telemetry.TelemetryBatcher +import software.aws.toolkits.core.telemetry.TelemetryPublisher +import software.aws.toolkits.jetbrains.core.MockClientManagerRule +import software.aws.toolkits.jetbrains.services.codewhisperer.CodeWhispererTestUtil.pythonFileName +import software.aws.toolkits.jetbrains.services.codewhisperer.CodeWhispererTestUtil.pythonTestLeftContext +import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererLoginType +import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.CodeWhispererExplorerActionManager +import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage +import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererPython +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC +import software.aws.toolkits.jetbrains.services.telemetry.NoOpPublisher +import software.aws.toolkits.jetbrains.services.telemetry.TelemetryService +import software.aws.toolkits.jetbrains.settings.AwsSettings +import software.aws.toolkits.jetbrains.utils.rules.CodeInsightTestFixtureRule +import software.aws.toolkits.jetbrains.utils.rules.PythonCodeInsightTestFixtureRule + +internal class UserWrittenCodeTrackerTest { + + protected class TestTelemetryService( + publisher: TelemetryPublisher = NoOpPublisher(), + batcher: TelemetryBatcher, + ) : TelemetryService(publisher, batcher) + + @Rule + @JvmField + val projectRule: CodeInsightTestFixtureRule + + @Rule + @JvmField + val disposableRule = DisposableRule() + + @Rule + @JvmField + val mockClientManagerRule = MockClientManagerRule() + + protected lateinit var project: Project + protected lateinit var fixture: CodeInsightTestFixture + protected lateinit var telemetryServiceSpy: TelemetryService + protected lateinit var batcher: TelemetryBatcher + protected lateinit var exploreActionManagerMock: CodeWhispererExplorerActionManager + protected lateinit var sut: UserWrittenCodeTracker + + init { + this.projectRule = PythonCodeInsightTestFixtureRule() + } + + @Before + open fun setup() { + this.project = projectRule.project + this.fixture = projectRule.fixture + fixture.configureByText(pythonFileName, pythonTestLeftContext) + AwsSettings.getInstance().isTelemetryEnabled = true + batcher = mock() + telemetryServiceSpy = spy(TestTelemetryService(batcher = batcher)) + + exploreActionManagerMock = mock { + on { checkActiveCodeWhispererConnectionType(any()) } doReturn CodeWhispererLoginType.Sono + } + + ApplicationManager.getApplication().replaceService(CodeWhispererExplorerActionManager::class.java, exploreActionManagerMock, disposableRule.disposable) + ApplicationManager.getApplication().replaceService(TelemetryService::class.java, telemetryServiceSpy, disposableRule.disposable) + + fixture.configureByText(pythonFileName, pythonTestLeftContext) + runInEdtAndWait { + projectRule.fixture.editor.caretModel.primaryCaret.moveToOffset(projectRule.fixture.editor.document.textLength) + } + } + + @After + fun tearDown() { + if (::sut.isInitialized) { + sut.forceTrackerFlush() + sut.reset() + } + } + + @Test + fun `test tracker is listening to q service invocation`() { + sut = UserWrittenCodeTracker.getInstance(project) + sut.activateTrackerIfNotActive() + assertThat(sut.qInvocationCount.get()).isEqualTo(0) + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC).onEvent(QFeatureEvent.INVOCATION) + assertThat(sut.qInvocationCount.get()).isEqualTo(1) + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC).onEvent(QFeatureEvent.INVOCATION) + assertThat(sut.qInvocationCount.get()).isEqualTo(2) + } + + + @Test + fun `test tracker is not listening to multi char input more than 50, but works for less than 50, and will not increment totalTokens - add new code`() { + sut = UserWrittenCodeTracker.getInstance(project) + sut.activateTrackerIfNotActive() + fixture.configureByText(pythonFileName, "") + val newCode = "def addTwoNumbers\n return" + runInEdtAndWait { + WriteCommandAction.runWriteCommandAction(project) { + fixture.editor.appendString(newCode) + } + } + val language: CodeWhispererProgrammingLanguage = CodeWhispererPython.INSTANCE + assertThat(sut.userWrittenCodeCharacterCount[language]).isEqualTo(newCode.length.toLong()) + assertThat(sut.userWrittenCodeLineCount[language]).isEqualTo(1) + + val anotherCode = "(x, y):\n".repeat(8) + runInEdtAndWait { + WriteCommandAction.runWriteCommandAction(project) { + fixture.editor.appendString(anotherCode) + } + } + assertThat(sut.userWrittenCodeCharacterCount[language]).isEqualTo(newCode.length.toLong()) + assertThat(sut.userWrittenCodeLineCount[language]).isEqualTo(1) + } + + @Test + fun `test tracker is listening to document changes and increment totalTokens - delete code should not affect`() { + sut = UserWrittenCodeTracker.getInstance(project) + sut.activateTrackerIfNotActive() + assertThat(sut.userWrittenCodeCharacterCount.getOrDefault(CodeWhispererPython.INSTANCE, 0)).isEqualTo(0) + runInEdtAndWait { + fixture.editor.caretModel.primaryCaret.moveToOffset(fixture.editor.document.textLength) + WriteCommandAction.runWriteCommandAction(project) { + fixture.editor.document.deleteString(fixture.editor.caretModel.offset - 3, fixture.editor.caretModel.offset) + } + } + assertThat(sut.userWrittenCodeCharacterCount.getOrDefault(CodeWhispererPython.INSTANCE, 0)).isEqualTo(0) + } + + + @Test + fun `test tracker is listening to document changes only when Q is not editing`() { + sut = UserWrittenCodeTracker.getInstance(project) + sut.activateTrackerIfNotActive() + fixture.configureByText(pythonFileName, "") + val newCode = "def addTwoNumbers\n return" + + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC).onEvent(QFeatureEvent.STARTS_EDITING) + runInEdtAndWait { + WriteCommandAction.runWriteCommandAction(project) { + fixture.editor.appendString(newCode) + } + } + + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC).onEvent(QFeatureEvent.FINISHES_EDITING) + val language: CodeWhispererProgrammingLanguage = CodeWhispererPython.INSTANCE + assertThat(sut.userWrittenCodeCharacterCount.getOrDefault(language, 0)).isEqualTo(0) + assertThat(sut.userWrittenCodeLineCount.getOrDefault(language, 0)).isEqualTo(0) + + runInEdtAndWait { + WriteCommandAction.runWriteCommandAction(project) { + fixture.editor.appendString(newCode) + } + } + assertThat(sut.userWrittenCodeCharacterCount[CodeWhispererPython.INSTANCE]).isEqualTo(newCode.length.toLong()) + assertThat(sut.userWrittenCodeLineCount[CodeWhispererPython.INSTANCE]).isEqualTo(1) + + } + + private fun Editor.appendString(string: String) { + val currentOffset = caretModel.primaryCaret.offset + document.insertString(currentOffset, string) + caretModel.moveToOffset(currentOffset + string.length) + } + +} From 3e05e4a592abc7bc15e9a9e7c481d63c0ea17291 Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Fri, 27 Dec 2024 12:42:17 -0800 Subject: [PATCH 10/21] adjust --- .../telemetry/UserWrittenCodeTracker.kt | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt index 7898318018..a340944941 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt @@ -42,8 +42,8 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { @Synchronized fun activateTrackerIfNotActive() { // tracker will only be activated if and only if IsTelemetryEnabled = true && isActive = false - if (!isTelemetryEnabled() || isActive.getAndSet(true)) return - + if (!isTelemetryEnabled() || isActive.get()) return + isActive.set(true) // count q service invocations val conn = ApplicationManager.getApplication().messageBus.connect() conn.subscribe( @@ -92,7 +92,7 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { internal fun documentChanged(event: DocumentEvent) { // do not listen to document changed made by Amazon Q itself - if (isQMakingEdits.get()) { + if (isQMakingEdits.get() || !isActive.get()) { return } @@ -109,7 +109,7 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { // when event is auto closing [{(', there will be 2 separated events, both count as 1 char increase in total chars val text = event.newFragment.toString() val lines = text.split('\n').size - 1 - if (event.newLength < COPY_THRESHOLD && text.trim().isNotEmpty()) { + if (event.newLength < COPY_THRESHOLD && !isIntelliJMultiSpacesInsert(text)) { // count doc changes from <50 multi character input as total user written code // ignore all white space changes, this usually comes from IntelliJ formatting val language = PsiDocumentManager.getInstance(project).getPsiFile(event.document)?.programmingLanguage() @@ -120,6 +120,11 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { } } + // intelliJ sometimes insert multi spaces for indentation, this is not user written code + private fun isIntelliJMultiSpacesInsert(text: String): Boolean { + return text.trim { it == ' ' }.isEmpty() && text.length > 1 + } + private fun emitCodeWhispererCodeContribution() { val customizationArn: String? = CodeWhispererModelConfigurator.getInstance().activeCustomization(project)?.arn @@ -136,8 +141,8 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { 0, 0, 0, - userWrittenCodeCharacterCount = userWrittenCodeCharacterCount[language], - userWrittenCodeLineCount = userWrittenCodeLineCount[(language)] + userWrittenCodeCharacterCount = userWrittenCodeCharacterCount.getOrDefault(language, 0), + userWrittenCodeLineCount = userWrittenCodeLineCount.getOrDefault(language, 0) ) LOG.debug { "Successfully sent code percentage telemetry. RequestId: ${response.responseMetadata().requestId()}" } } catch (e: Exception) { From c8b29fec8e5079cb68627112c43be6403912e4ff Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Fri, 27 Dec 2024 12:47:54 -0800 Subject: [PATCH 11/21] update if else cond --- .../services/codewhisperer/telemetry/UserWrittenCodeTracker.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt index a340944941..2ebed700ee 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt @@ -109,7 +109,7 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { // when event is auto closing [{(', there will be 2 separated events, both count as 1 char increase in total chars val text = event.newFragment.toString() val lines = text.split('\n').size - 1 - if (event.newLength < COPY_THRESHOLD && !isIntelliJMultiSpacesInsert(text)) { + if (event.newLength < COPY_THRESHOLD && !isIntelliJMultiSpacesInsert(text) && text.isNotEmpty()) { // count doc changes from <50 multi character input as total user written code // ignore all white space changes, this usually comes from IntelliJ formatting val language = PsiDocumentManager.getInstance(project).getPsiFile(event.document)?.programmingLanguage() From b2a60fd582659dc80d1dbc11c6d5fa0670c6f43f Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Mon, 6 Jan 2025 12:56:28 -0800 Subject: [PATCH 12/21] fix detekt --- .../editor/CodeWhispererEditorManagerNew.kt | 1 - .../telemetry/UserWrittenCodeTracker.kt | 15 ++++--------- .../UserWrittenCodeTrackerTest.kt | 22 ++++++------------- 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt index 9a86dc4c83..8a14e18cc5 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt @@ -53,7 +53,6 @@ class CodeWhispererEditorManagerNew { preview.detail.isAccepted = true WriteCommandAction.runWriteCommandAction(project) { - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) .onEvent(QFeatureEvent.STARTS_EDITING) document.replaceString(originalOffset, endOffsetToReplace, reformatted) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt index 2ebed700ee..c67e47968a 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt @@ -27,7 +27,6 @@ import java.time.Duration import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger - @Service(Service.Level.PROJECT) class UserWrittenCodeTracker(private val project: Project) : Disposable { val userWrittenCodeLineCount = mutableMapOf() @@ -50,7 +49,7 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { Q_FEATURE_TOPIC, object : QFeatureListener { override fun onEvent(event: QFeatureEvent) { - when(event) { + when (event) { QFeatureEvent.INVOCATION -> qInvocationCount.getAndIncrement() QFeatureEvent.STARTS_EDITING -> isQMakingEdits.set(true) QFeatureEvent.FINISHES_EDITING -> isQMakingEdits.set(false) @@ -121,15 +120,12 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { } // intelliJ sometimes insert multi spaces for indentation, this is not user written code - private fun isIntelliJMultiSpacesInsert(text: String): Boolean { - return text.trim { it == ' ' }.isEmpty() && text.length > 1 - } - + private fun isIntelliJMultiSpacesInsert(text: String) = text.trim { it == ' ' }.isEmpty() && text.length > 1 private fun emitCodeWhispererCodeContribution() { val customizationArn: String? = CodeWhispererModelConfigurator.getInstance().activeCustomization(project)?.arn for ((language, _) in userWrittenCodeCharacterCount) { - if (userWrittenCodeCharacterCount.getOrDefault(language, 0) <= 0 ) { + if (userWrittenCodeCharacterCount.getOrDefault(language, 0) <= 0) { continue } runIfIdcConnectionOrTelemetryEnabled(project) { @@ -153,7 +149,6 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { } } } - } companion object { @@ -168,7 +163,6 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { "Q service events", QFeatureListener::class.java ) - } override fun dispose() { @@ -187,10 +181,9 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { enum class QFeatureEvent { INVOCATION, STARTS_EDITING, - FINISHES_EDITING + FINISHES_EDITING, } interface QFeatureListener { fun onEvent(event: QFeatureEvent) } - diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt index e845897fc4..cc16a0e4ef 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt @@ -6,7 +6,6 @@ package software.aws.toolkits.jetbrains.services.codewhisperer import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.editor.Editor -import com.intellij.openapi.editor.event.DocumentEvent import com.intellij.openapi.project.Project import com.intellij.testFramework.DisposableRule import com.intellij.testFramework.fixtures.CodeInsightTestFixture @@ -17,13 +16,10 @@ import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test -import org.mockito.internal.verification.Times import org.mockito.kotlin.any -import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.spy -import org.mockito.kotlin.verify import software.aws.toolkits.core.telemetry.TelemetryBatcher import software.aws.toolkits.core.telemetry.TelemetryPublisher import software.aws.toolkits.jetbrains.core.MockClientManagerRule @@ -44,7 +40,7 @@ import software.aws.toolkits.jetbrains.utils.rules.PythonCodeInsightTestFixtureR internal class UserWrittenCodeTrackerTest { - protected class TestTelemetryService( + internal class TestTelemetryService( publisher: TelemetryPublisher = NoOpPublisher(), batcher: TelemetryBatcher, ) : TelemetryService(publisher, batcher) @@ -61,12 +57,12 @@ internal class UserWrittenCodeTrackerTest { @JvmField val mockClientManagerRule = MockClientManagerRule() - protected lateinit var project: Project - protected lateinit var fixture: CodeInsightTestFixture - protected lateinit var telemetryServiceSpy: TelemetryService - protected lateinit var batcher: TelemetryBatcher - protected lateinit var exploreActionManagerMock: CodeWhispererExplorerActionManager - protected lateinit var sut: UserWrittenCodeTracker + lateinit var project: Project + lateinit var fixture: CodeInsightTestFixture + lateinit var telemetryServiceSpy: TelemetryService + lateinit var batcher: TelemetryBatcher + lateinit var exploreActionManagerMock: CodeWhispererExplorerActionManager + lateinit var sut: UserWrittenCodeTracker init { this.projectRule = PythonCodeInsightTestFixtureRule() @@ -113,7 +109,6 @@ internal class UserWrittenCodeTrackerTest { assertThat(sut.qInvocationCount.get()).isEqualTo(2) } - @Test fun `test tracker is not listening to multi char input more than 50, but works for less than 50, and will not increment totalTokens - add new code`() { sut = UserWrittenCodeTracker.getInstance(project) @@ -153,7 +148,6 @@ internal class UserWrittenCodeTrackerTest { assertThat(sut.userWrittenCodeCharacterCount.getOrDefault(CodeWhispererPython.INSTANCE, 0)).isEqualTo(0) } - @Test fun `test tracker is listening to document changes only when Q is not editing`() { sut = UserWrittenCodeTracker.getInstance(project) @@ -180,7 +174,6 @@ internal class UserWrittenCodeTrackerTest { } assertThat(sut.userWrittenCodeCharacterCount[CodeWhispererPython.INSTANCE]).isEqualTo(newCode.length.toLong()) assertThat(sut.userWrittenCodeLineCount[CodeWhispererPython.INSTANCE]).isEqualTo(1) - } private fun Editor.appendString(string: String) { @@ -188,5 +181,4 @@ internal class UserWrittenCodeTrackerTest { document.insertString(currentOffset, string) caretModel.moveToOffset(currentOffset + string.length) } - } From 25fa105e4a23ae87797d45887debe1556716881f Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Mon, 6 Jan 2025 13:24:24 -0800 Subject: [PATCH 13/21] :plugin-amazonq:chat:jetbrains-community:detekt fix --- .../jetbrains/services/cwc/controller/ChatController.kt | 4 ---- .../jetbrains/services/cwc/inline/InlineChatController.kt | 1 - 2 files changed, 5 deletions(-) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt index ad4d1ca267..e92c44b636 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt @@ -217,7 +217,6 @@ class ChatController private constructor( } override suspend fun processInsertCodeAtCursorPosition(message: IncomingCwcMessage.InsertCodeAtCursorPosition) { - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) .onEvent(QFeatureEvent.STARTS_EDITING) withContext(EDT) { @@ -567,8 +566,5 @@ class ChatController private constructor( .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) .setSerializationInclusion(JsonInclude.Include.NON_NULL) - - } } - diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt index ccdd03ade0..9606472d0b 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt @@ -552,7 +552,6 @@ class InlineChatController( } private fun replaceString(document: Document, start: Int, end: Int, text: String) { - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) .onEvent(QFeatureEvent.STARTS_EDITING) ApplicationManager.getApplication().invokeAndWait { From 3a8c24f4ec6eb66c3ddfaa4d08b7b7ba115ede80 Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Tue, 21 Jan 2025 10:15:24 -0800 Subject: [PATCH 14/21] resolve some feedback comments --- .../controller/CodeScanChatHelper.kt | 4 ++-- .../controller/CodeTestChatController.kt | 4 ++-- .../amazonqDoc/controller/DocController.kt | 4 ++-- .../controller/FeatureDevController.kt | 10 ++++------ .../services/cwc/controller/ChatController.kt | 10 ++++------ .../chat/messenger/ChatPromptHandler.kt | 4 ++-- .../services/cwc/inline/InlineChatController.kt | 16 ++++++---------- .../controller/CodeTransformChatController.kt | 4 ++-- .../utils/CodeWhispererCodeScanIssueUtils.kt | 7 +++---- .../editor/CodeWhispererEditorManager.kt | 7 +++---- .../editor/CodeWhispererEditorManagerNew.kt | 7 +++---- .../service/CodeWhispererService.kt | 4 ++-- .../telemetry/UserWrittenCodeTracker.kt | 12 ++++++++++-- 13 files changed, 45 insertions(+), 48 deletions(-) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeScan/controller/CodeScanChatHelper.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeScan/controller/CodeScanChatHelper.kt index 9c505afcb5..31cb656ce5 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeScan/controller/CodeScanChatHelper.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeScan/controller/CodeScanChatHelper.kt @@ -14,6 +14,7 @@ import software.aws.toolkits.jetbrains.services.amazonqCodeScan.messages.UpdateP import software.aws.toolkits.jetbrains.services.amazonqCodeScan.storage.ChatSessionStorage import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.cwc.messages.ChatMessageType import java.util.UUID @@ -37,8 +38,7 @@ class CodeScanChatHelper( clearPreviousItemButtons: Boolean? = false, ) { if (isInValidSession()) return - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.INVOCATION) + broadcastQEvent(QFeatureEvent.INVOCATION) messagePublisher.publish( CodeScanChatMessage( tabId = activeCodeScanTabId as String, diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt index 6cb9528819..e64b1ed711 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt @@ -73,6 +73,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhisp import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmingLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.codewhisperer.toolwindow.CodeWhispererCodeReferenceManager import software.aws.toolkits.jetbrains.services.cwc.ChatConstants import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatRequestData @@ -1206,8 +1207,7 @@ class CodeTestChatController( "Processing message: $message " + "tabId: $tabId" } - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.INVOCATION) + broadcastQEvent(QFeatureEvent.INVOCATION) when (session.conversationState) { ConversationState.WAITING_FOR_BUILD_COMMAND_INPUT -> handleBuildCommandInput(session, message) ConversationState.WAITING_FOR_REGENERATE_INPUT -> handleRegenerateInput(session, message) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt index 3e377042fa..895331f9c9 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt @@ -73,6 +73,7 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.Sessio import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.CancellationTokenSource import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.resources.message import java.nio.file.Paths import java.util.UUID @@ -721,8 +722,7 @@ class DocController( is PrepareDocGenerationState -> state.filePaths else -> emptyList() } - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.INVOCATION) + broadcastQEvent(QFeatureEvent.INVOCATION) if (filePaths.isNotEmpty()) { processOpenDiff( diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt index 2252265e63..7f62768a39 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt @@ -74,6 +74,7 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.InsertAct import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.getFollowUpOptions import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.codewhisperer.util.content import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.FeedbackComment import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl @@ -194,8 +195,7 @@ class FeatureDevController( logger.debug { "$FEATURE_NAME: Processing InsertCodeAtCursorPosition: $message" } withContext(EDT) { - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.STARTS_EDITING) + broadcastQEvent(QFeatureEvent.STARTS_EDITING) val editor: Editor = FileEditorManager.getInstance(context.project).selectedTextEditor ?: return@withContext val caret: Caret = editor.caretModel.primaryCaret @@ -207,8 +207,7 @@ class FeatureDevController( } editor.document.insertString(offset, message.code) } - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.FINISHES_EDITING) + broadcastQEvent(QFeatureEvent.FINISHES_EDITING) } } @@ -686,8 +685,7 @@ class FeatureDevController( } session.preloader(message, messenger) - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.INVOCATION) + broadcastQEvent(QFeatureEvent.INVOCATION) when (session.sessionState.phase) { SessionStatePhase.CODEGEN -> onCodeGeneration(session, message, tabId) else -> null diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt index dd9be8c977..e0e5c142dd 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt @@ -51,6 +51,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhisp import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererUserModificationTracker import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.cwc.InboundAppMessagesHandler import software.aws.toolkits.jetbrains.services.cwc.clients.chat.exceptions.ChatApiException import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatRequestData @@ -217,8 +218,7 @@ class ChatController private constructor( } override suspend fun processInsertCodeAtCursorPosition(message: IncomingCwcMessage.InsertCodeAtCursorPosition) { - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.STARTS_EDITING) + broadcastQEvent(QFeatureEvent.STARTS_EDITING) withContext(EDT) { val editor: Editor = FileEditorManager.getInstance(context.project).selectedTextEditor ?: return@withContext @@ -250,8 +250,7 @@ class ChatController private constructor( } telemetryHelper.recordInteractWithMessage(message) - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.FINISHES_EDITING) + broadcastQEvent(QFeatureEvent.FINISHES_EDITING) } override suspend fun processStopResponseMessage(message: IncomingCwcMessage.StopResponse) { @@ -445,8 +444,7 @@ class ChatController private constructor( sessionInfo.history.add(requestData) telemetryHelper.recordEnterFocusConversation(tabId) telemetryHelper.recordStartConversation(tabId, requestData) - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.INVOCATION) + broadcastQEvent(QFeatureEvent.INVOCATION) // Send the request to the API and publish the responses back to the UI. // This is launched in a scope attached to the sessionInfo so that the Job can be cancelled on a per-session basis. ChatPromptHandler(telemetryHelper).handle(tabId, triggerId, requestData, sessionInfo, shouldAddIndexInProgressMessage) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/messenger/ChatPromptHandler.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/messenger/ChatPromptHandler.kt index b1abef3bff..c9e2ce1f1e 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/messenger/ChatPromptHandler.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/messenger/ChatPromptHandler.kt @@ -16,6 +16,7 @@ import software.aws.toolkits.core.utils.convertMarkdownToHTML import software.aws.toolkits.core.utils.extractCodeBlockLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.cwc.clients.chat.exceptions.ChatApiException import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatRequestData import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatResponseEvent @@ -119,8 +120,7 @@ class ChatPromptHandler(private val telemetryHelper: TelemetryHelper) { telemetryHelper.recordAddMessage(data, response, responseText.length, statusCode, countTotalNumberOfCodeBlocks(responseText)) emit(response) - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.INVOCATION) + broadcastQEvent(QFeatureEvent.INVOCATION) } .catch { exception -> val statusCode = if (exception is AwsServiceException) exception.statusCode() else 0 diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt index 9606472d0b..00f6fc17d8 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt @@ -60,6 +60,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.customization.Code import software.aws.toolkits.jetbrains.services.codewhisperer.model.CaretPosition import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatRequestData import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.TriggerType import software.aws.toolkits.jetbrains.services.cwc.controller.ReferenceLogController @@ -535,8 +536,7 @@ class InlineChatController( private fun insertString(editor: Editor, offset: Int, text: String): RangeMarker { lateinit var rangeMarker: RangeMarker - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.STARTS_EDITING) + broadcastQEvent(QFeatureEvent.STARTS_EDITING) ApplicationManager.getApplication().invokeAndWait { CommandProcessor.getInstance().runUndoTransparentAction { WriteCommandAction.runWriteCommandAction(project) { @@ -546,14 +546,12 @@ class InlineChatController( highlightCodeWithBackgroundColor(editor, rangeMarker.startOffset, rangeMarker.endOffset, true) } } - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.FINISHES_EDITING) + broadcastQEvent(QFeatureEvent.FINISHES_EDITING) return rangeMarker } private fun replaceString(document: Document, start: Int, end: Int, text: String) { - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.STARTS_EDITING) + broadcastQEvent(QFeatureEvent.STARTS_EDITING) ApplicationManager.getApplication().invokeAndWait { CommandProcessor.getInstance().runUndoTransparentAction { WriteCommandAction.runWriteCommandAction(project) { @@ -561,8 +559,7 @@ class InlineChatController( } } } - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.FINISHES_EDITING) + broadcastQEvent(QFeatureEvent.FINISHES_EDITING) } private fun highlightString(editor: Editor, start: Int, end: Int, isInsert: Boolean) { @@ -720,8 +717,7 @@ class InlineChatController( undoChanges() } - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.FINISHES_EDITING) + broadcastQEvent(QFeatureEvent.FINISHES_EDITING) return errorMessage } diff --git a/plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/controller/CodeTransformChatController.kt b/plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/controller/CodeTransformChatController.kt index 2512601ba8..0ef64a9ab8 100644 --- a/plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/controller/CodeTransformChatController.kt +++ b/plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/controller/CodeTransformChatController.kt @@ -114,6 +114,7 @@ import software.aws.toolkits.jetbrains.services.codemodernizer.utils.unzipFile import software.aws.toolkits.jetbrains.services.codemodernizer.utils.validateSctMetadata import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.cwc.messages.ChatMessageType import software.aws.toolkits.resources.message @@ -139,8 +140,7 @@ class CodeTransformChatController( if (objective == "language upgrade" || objective == "sql conversion") { telemetry.submitSelection(objective) } - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.INVOCATION) + broadcastQEvent(QFeatureEvent.INVOCATION) when (objective) { "language upgrade" -> this.handleLanguageUpgrade() "sql conversion" -> this.handleSQLConversion() diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/utils/CodeWhispererCodeScanIssueUtils.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/utils/CodeWhispererCodeScanIssueUtils.kt index 870040b8bc..17be248405 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/utils/CodeWhispererCodeScanIssueUtils.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/utils/CodeWhispererCodeScanIssueUtils.kt @@ -40,6 +40,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmi import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.codewhisperer.toolwindow.CodeWhispererCodeReferenceManager import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.CODE_SCAN_ISSUE_TITLE_MAX_LENGTH @@ -333,8 +334,7 @@ fun applySuggestedFix(project: Project, issue: CodeWhispererCodeScanIssue) { try { val manager = CodeWhispererCodeReferenceManager.getInstance(issue.project) WriteCommandAction.runWriteCommandAction(issue.project) { - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.STARTS_EDITING) + broadcastQEvent(QFeatureEvent.STARTS_EDITING) val document = FileDocumentManager.getInstance().getDocument(issue.file) ?: return@runWriteCommandAction val documentContent = document.text @@ -347,8 +347,7 @@ fun applySuggestedFix(project: Project, issue: CodeWhispererCodeScanIssue) { LOG.debug { "Original content from reference span: $originalContent" } manager.addReferenceLogPanelEntry(reference = reference, null, null, originalContent.split("\n")) } - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.FINISHES_EDITING) + broadcastQEvent(QFeatureEvent.FINISHES_EDITING) } if (issue.suggestedFixes[0].references.isNotEmpty()) { manager.toolWindow?.show() diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt index 2f3808da1b..39cba96cfe 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt @@ -17,6 +17,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispere import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.codewhisperer.util.CaretMovement import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.PAIRED_BRACKETS import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.PAIRED_QUOTES @@ -46,14 +47,12 @@ class CodeWhispererEditorManager { val endOffsetToReplace = if (insertEndOffset != -1) insertEndOffset else primaryCaret.offset WriteCommandAction.runWriteCommandAction(project) { - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.STARTS_EDITING) + broadcastQEvent(QFeatureEvent.STARTS_EDITING) document.replaceString(originalOffset, endOffsetToReplace, reformatted) PsiDocumentManager.getInstance(project).commitDocument(document) primaryCaret.moveToOffset(endOffset + detail.rightOverlap.length) - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.FINISHES_EDITING) + broadcastQEvent(QFeatureEvent.FINISHES_EDITING) } ApplicationManager.getApplication().invokeLater { diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt index 8a14e18cc5..6dfe5075ee 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt @@ -18,6 +18,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispe import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryServiceNew import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.codewhisperer.util.CaretMovement import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.PAIRED_BRACKETS import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.PAIRED_QUOTES @@ -53,14 +54,12 @@ class CodeWhispererEditorManagerNew { preview.detail.isAccepted = true WriteCommandAction.runWriteCommandAction(project) { - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.STARTS_EDITING) + broadcastQEvent(QFeatureEvent.STARTS_EDITING) document.replaceString(originalOffset, endOffsetToReplace, reformatted) PsiDocumentManager.getInstance(project).commitDocument(document) primaryCaret.moveToOffset(endOffset + detail.rightOverlap.length) - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.FINISHES_EDITING) + broadcastQEvent(QFeatureEvent.FINISHES_EDITING) } ApplicationManager.getApplication().invokeLater { diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt index 7a1dec74c7..2f6ded04b2 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt @@ -76,6 +76,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispere import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.codewhisperer.util.CaretMovement import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeInsightsSettingsFacade import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants @@ -263,8 +264,7 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable { lastRecommendationIndex += response.completions().size ApplicationManager.getApplication().messageBus.syncPublisher(CODEWHISPERER_CODE_COMPLETION_PERFORMED) .onSuccess(requestContext.fileContextInfo) - ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) - .onEvent(QFeatureEvent.INVOCATION) + broadcastQEvent(QFeatureEvent.INVOCATION) CodeWhispererTelemetryService.getInstance().sendServiceInvocationEvent( requestId, requestContext, diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt index c67e47968a..d2f3466339 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt @@ -12,6 +12,7 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiDocumentManager import com.intellij.util.Alarm import com.intellij.util.AlarmFactory +import com.intellij.util.messages.MessageBusConnection import com.intellij.util.messages.Topic import org.jetbrains.annotations.TestOnly import software.amazon.awssdk.services.codewhispererruntime.model.CodeWhispererRuntimeException @@ -21,6 +22,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWh import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererModelConfigurator import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmingLanguage +import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.util.runIfIdcConnectionOrTelemetryEnabled import software.aws.toolkits.jetbrains.settings.AwsSettings import java.time.Duration @@ -37,6 +39,7 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { val qInvocationCount: AtomicInteger = AtomicInteger(0) private val isQMakingEdits = AtomicBoolean(false) private val isActive: AtomicBoolean = AtomicBoolean(false) + private var conn: MessageBusConnection? = null @Synchronized fun activateTrackerIfNotActive() { @@ -44,8 +47,8 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { if (!isTelemetryEnabled() || isActive.get()) return isActive.set(true) // count q service invocations - val conn = ApplicationManager.getApplication().messageBus.connect() - conn.subscribe( + conn = ApplicationManager.getApplication().messageBus.connect() + conn?.subscribe( Q_FEATURE_TOPIC, object : QFeatureListener { override fun onEvent(event: QFeatureEvent) { @@ -169,6 +172,7 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { if (isShuttingDown.getAndSet(true)) { return } + conn?.disconnect() flush() } @@ -187,3 +191,7 @@ enum class QFeatureEvent { interface QFeatureListener { fun onEvent(event: QFeatureEvent) } + +fun broadcastQEvent(event: QFeatureEvent) = + ApplicationManager.getApplication().messageBus.syncPublisher(Q_FEATURE_TOPIC) + .onEvent(event) From 937e53de61f368720a40f74a97e594414cb41368 Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Tue, 21 Jan 2025 10:26:12 -0800 Subject: [PATCH 15/21] revise unit test --- .../codewhisperer/UserWrittenCodeTrackerTest.kt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt index cc16a0e4ef..7569533cf6 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt @@ -47,15 +47,16 @@ internal class UserWrittenCodeTrackerTest { @Rule @JvmField - val projectRule: CodeInsightTestFixtureRule + val disposableRule = DisposableRule() @Rule @JvmField - val disposableRule = DisposableRule() + val mockClientManagerRule = MockClientManagerRule() + @Rule @JvmField - val mockClientManagerRule = MockClientManagerRule() + var projectRule = PythonCodeInsightTestFixtureRule() lateinit var project: Project lateinit var fixture: CodeInsightTestFixture @@ -64,9 +65,6 @@ internal class UserWrittenCodeTrackerTest { lateinit var exploreActionManagerMock: CodeWhispererExplorerActionManager lateinit var sut: UserWrittenCodeTracker - init { - this.projectRule = PythonCodeInsightTestFixtureRule() - } @Before open fun setup() { @@ -75,14 +73,12 @@ internal class UserWrittenCodeTrackerTest { fixture.configureByText(pythonFileName, pythonTestLeftContext) AwsSettings.getInstance().isTelemetryEnabled = true batcher = mock() - telemetryServiceSpy = spy(TestTelemetryService(batcher = batcher)) exploreActionManagerMock = mock { on { checkActiveCodeWhispererConnectionType(any()) } doReturn CodeWhispererLoginType.Sono } ApplicationManager.getApplication().replaceService(CodeWhispererExplorerActionManager::class.java, exploreActionManagerMock, disposableRule.disposable) - ApplicationManager.getApplication().replaceService(TelemetryService::class.java, telemetryServiceSpy, disposableRule.disposable) fixture.configureByText(pythonFileName, pythonTestLeftContext) runInEdtAndWait { From d359a845213ebc63e172c5f836c4e4a8d3dbd555 Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Tue, 21 Jan 2025 10:47:18 -0800 Subject: [PATCH 16/21] add read access assert --- .../services/codewhisperer/UserWrittenCodeTrackerTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt index 7569533cf6..2e345a2db7 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt @@ -128,6 +128,7 @@ internal class UserWrittenCodeTrackerTest { } assertThat(sut.userWrittenCodeCharacterCount[language]).isEqualTo(newCode.length.toLong()) assertThat(sut.userWrittenCodeLineCount[language]).isEqualTo(1) + ApplicationManager.getApplication().assertReadAccessAllowed() } @Test @@ -142,6 +143,7 @@ internal class UserWrittenCodeTrackerTest { } } assertThat(sut.userWrittenCodeCharacterCount.getOrDefault(CodeWhispererPython.INSTANCE, 0)).isEqualTo(0) + ApplicationManager.getApplication().assertReadAccessAllowed() } @Test From 82cba1811938e9e444c28cc76088f92910deb521 Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Tue, 21 Jan 2025 10:49:26 -0800 Subject: [PATCH 17/21] fix detekt --- .../services/amazonqCodeScan/controller/CodeScanChatHelper.kt | 2 -- .../amazonqCodeTest/controller/CodeTestChatController.kt | 1 - .../jetbrains/services/amazonqDoc/controller/DocController.kt | 2 -- .../amazonqFeatureDev/controller/FeatureDevController.kt | 2 -- .../jetbrains/services/cwc/controller/ChatController.kt | 1 - .../services/cwc/controller/chat/messenger/ChatPromptHandler.kt | 2 -- .../jetbrains/services/cwc/inline/InlineChatController.kt | 1 - 7 files changed, 11 deletions(-) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeScan/controller/CodeScanChatHelper.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeScan/controller/CodeScanChatHelper.kt index 31cb656ce5..f135cfa18b 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeScan/controller/CodeScanChatHelper.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeScan/controller/CodeScanChatHelper.kt @@ -3,7 +3,6 @@ package software.aws.toolkits.jetbrains.services.amazonqCodeScan.controller -import com.intellij.openapi.application.ApplicationManager import software.aws.toolkits.jetbrains.services.amazonq.messages.MessagePublisher import software.aws.toolkits.jetbrains.services.amazonqCodeScan.buildClearPromptProgressMessage import software.aws.toolkits.jetbrains.services.amazonqCodeScan.buildPromptProgressMessage @@ -13,7 +12,6 @@ import software.aws.toolkits.jetbrains.services.amazonqCodeScan.messages.CodeSca import software.aws.toolkits.jetbrains.services.amazonqCodeScan.messages.UpdatePlaceholderMessage import software.aws.toolkits.jetbrains.services.amazonqCodeScan.storage.ChatSessionStorage import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent -import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.cwc.messages.ChatMessageType import java.util.UUID diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt index e64b1ed711..04646d3b81 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt @@ -72,7 +72,6 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWh import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmingLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent -import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.codewhisperer.toolwindow.CodeWhispererCodeReferenceManager import software.aws.toolkits.jetbrains.services.cwc.ChatConstants diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt index 895331f9c9..9482569101 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt @@ -9,7 +9,6 @@ import com.intellij.diff.contents.EmptyContent import com.intellij.diff.requests.SimpleDiffRequest import com.intellij.diff.util.DiffUserDataKeys import com.intellij.ide.BrowserUtil -import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.runInEdt import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ProjectRootManager @@ -72,7 +71,6 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.NewFil import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.SessionStatePhase import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.CancellationTokenSource import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent -import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.resources.message import java.nio.file.Paths diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt index 7f62768a39..cead910669 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt @@ -11,7 +11,6 @@ import com.intellij.diff.editor.ChainDiffVirtualFile import com.intellij.diff.editor.DiffEditorTabFilesManager import com.intellij.diff.requests.SimpleDiffRequest import com.intellij.ide.BrowserUtil -import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.runInEdt import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.editor.Caret @@ -73,7 +72,6 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.storage.ChatSe import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.InsertAction import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.getFollowUpOptions import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent -import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.codewhisperer.util.content import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.FeedbackComment diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt index e0e5c142dd..fc5450e247 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt @@ -50,7 +50,6 @@ import software.aws.toolkits.jetbrains.services.amazonq.project.RelevantDocument import software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhispererConfigurable import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererUserModificationTracker import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent -import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.cwc.InboundAppMessagesHandler import software.aws.toolkits.jetbrains.services.cwc.clients.chat.exceptions.ChatApiException diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/messenger/ChatPromptHandler.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/messenger/ChatPromptHandler.kt index c9e2ce1f1e..72c2f4a035 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/messenger/ChatPromptHandler.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/messenger/ChatPromptHandler.kt @@ -3,7 +3,6 @@ package software.aws.toolkits.jetbrains.services.cwc.controller.chat.messenger -import com.intellij.openapi.application.ApplicationManager import kotlinx.coroutines.CancellationException import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow @@ -15,7 +14,6 @@ import software.amazon.awssdk.services.codewhispererstreaming.model.CodeWhispere import software.aws.toolkits.core.utils.convertMarkdownToHTML import software.aws.toolkits.core.utils.extractCodeBlockLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent -import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.cwc.clients.chat.exceptions.ChatApiException import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatRequestData diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt index 00f6fc17d8..1a6f8d2d10 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/InlineChatController.kt @@ -59,7 +59,6 @@ import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AMAZON_Q_WIND import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererModelConfigurator import software.aws.toolkits.jetbrains.services.codewhisperer.model.CaretPosition import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent -import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.ChatRequestData import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.TriggerType From 79631b26a67c928a16d8c87a02e8f0d05f6d977e Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Tue, 21 Jan 2025 11:55:08 -0800 Subject: [PATCH 18/21] fix import error --- .../codemodernizer/controller/CodeTransformChatController.kt | 2 -- .../codescan/utils/CodeWhispererCodeScanIssueUtils.kt | 1 - .../services/codewhisperer/editor/CodeWhispererEditorManager.kt | 1 - .../codewhisperer/editor/CodeWhispererEditorManagerNew.kt | 1 - .../services/codewhisperer/service/CodeWhispererService.kt | 1 - 5 files changed, 6 deletions(-) diff --git a/plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/controller/CodeTransformChatController.kt b/plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/controller/CodeTransformChatController.kt index 0ef64a9ab8..16be893703 100644 --- a/plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/controller/CodeTransformChatController.kt +++ b/plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/controller/CodeTransformChatController.kt @@ -4,7 +4,6 @@ package software.aws.toolkits.jetbrains.services.codemodernizer.controller import com.intellij.ide.BrowserUtil -import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.runInEdt import com.intellij.openapi.fileChooser.FileChooser import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory @@ -113,7 +112,6 @@ import software.aws.toolkits.jetbrains.services.codemodernizer.utils.tryGetJdk import software.aws.toolkits.jetbrains.services.codemodernizer.utils.unzipFile import software.aws.toolkits.jetbrains.services.codemodernizer.utils.validateSctMetadata import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent -import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.cwc.messages.ChatMessageType import software.aws.toolkits.resources.message diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/utils/CodeWhispererCodeScanIssueUtils.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/utils/CodeWhispererCodeScanIssueUtils.kt index 17be248405..ff1c0c72c6 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/utils/CodeWhispererCodeScanIssueUtils.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/utils/CodeWhispererCodeScanIssueUtils.kt @@ -39,7 +39,6 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhisp import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmingLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent -import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.codewhisperer.toolwindow.CodeWhispererCodeReferenceManager import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt index 39cba96cfe..095a3e95e0 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt @@ -16,7 +16,6 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.model.SessionConte import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispererPopupManager import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent -import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.codewhisperer.util.CaretMovement import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.PAIRED_BRACKETS diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt index 6dfe5075ee..8e590fbba3 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManagerNew.kt @@ -17,7 +17,6 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispere import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererServiceNew import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryServiceNew import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent -import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.codewhisperer.util.CaretMovement import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.PAIRED_BRACKETS diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt index 2f6ded04b2..54451e949c 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt @@ -75,7 +75,6 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.model.WorkerContex import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispererPopupManager import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent -import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrittenCodeTracker.Companion.Q_FEATURE_TOPIC import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent import software.aws.toolkits.jetbrains.services.codewhisperer.util.CaretMovement import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeInsightsSettingsFacade From 35dc2e7d62178be7172e74681606ecbc75237b2a Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Tue, 21 Jan 2025 13:40:12 -0800 Subject: [PATCH 19/21] fix detekt --- .../services/codewhisperer/UserWrittenCodeTrackerTest.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt index 2e345a2db7..0119c72b2d 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt @@ -19,7 +19,6 @@ import org.junit.Test import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock -import org.mockito.kotlin.spy import software.aws.toolkits.core.telemetry.TelemetryBatcher import software.aws.toolkits.core.telemetry.TelemetryPublisher import software.aws.toolkits.jetbrains.core.MockClientManagerRule @@ -35,7 +34,6 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.UserWrit import software.aws.toolkits.jetbrains.services.telemetry.NoOpPublisher import software.aws.toolkits.jetbrains.services.telemetry.TelemetryService import software.aws.toolkits.jetbrains.settings.AwsSettings -import software.aws.toolkits.jetbrains.utils.rules.CodeInsightTestFixtureRule import software.aws.toolkits.jetbrains.utils.rules.PythonCodeInsightTestFixtureRule internal class UserWrittenCodeTrackerTest { @@ -53,7 +51,6 @@ internal class UserWrittenCodeTrackerTest { @JvmField val mockClientManagerRule = MockClientManagerRule() - @Rule @JvmField var projectRule = PythonCodeInsightTestFixtureRule() @@ -65,7 +62,6 @@ internal class UserWrittenCodeTrackerTest { lateinit var exploreActionManagerMock: CodeWhispererExplorerActionManager lateinit var sut: UserWrittenCodeTracker - @Before open fun setup() { this.project = projectRule.project From 6ec3f7c27e7439956d81a017b12a375a2edba440 Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Tue, 21 Jan 2025 15:23:36 -0800 Subject: [PATCH 20/21] fix unit test --- .../services/codewhisperer/UserWrittenCodeTrackerTest.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt index 0119c72b2d..c01b70025d 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/UserWrittenCodeTrackerTest.kt @@ -124,7 +124,6 @@ internal class UserWrittenCodeTrackerTest { } assertThat(sut.userWrittenCodeCharacterCount[language]).isEqualTo(newCode.length.toLong()) assertThat(sut.userWrittenCodeLineCount[language]).isEqualTo(1) - ApplicationManager.getApplication().assertReadAccessAllowed() } @Test @@ -139,7 +138,6 @@ internal class UserWrittenCodeTrackerTest { } } assertThat(sut.userWrittenCodeCharacterCount.getOrDefault(CodeWhispererPython.INSTANCE, 0)).isEqualTo(0) - ApplicationManager.getApplication().assertReadAccessAllowed() } @Test From 2a73168ddad9d83bfbd0f514ea323a94168c5d47 Mon Sep 17 00:00:00 2001 From: Lei Gao Date: Tue, 21 Jan 2025 15:48:17 -0800 Subject: [PATCH 21/21] add RequireReadlock --- .../services/codewhisperer/telemetry/UserWrittenCodeTracker.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt index d2f3466339..5bcdd289c0 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt @@ -12,6 +12,7 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiDocumentManager import com.intellij.util.Alarm import com.intellij.util.AlarmFactory +import com.intellij.util.concurrency.annotations.RequiresReadLock import com.intellij.util.messages.MessageBusConnection import com.intellij.util.messages.Topic import org.jetbrains.annotations.TestOnly @@ -92,6 +93,7 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable { } } + @RequiresReadLock internal fun documentChanged(event: DocumentEvent) { // do not listen to document changed made by Amazon Q itself if (isQMakingEdits.get() || !isActive.get()) {