@@ -21,12 +21,13 @@ import com.intellij.psi.PsiDocumentManager
21
21
import com.intellij.psi.PsiFile
22
22
import com.intellij.util.concurrency.annotations.RequiresEdt
23
23
import com.intellij.util.messages.Topic
24
- import kotlinx.coroutines.TimeoutCancellationException
24
+ import kotlinx.coroutines.CoroutineScope
25
+ import kotlinx.coroutines.Deferred
26
+ import kotlinx.coroutines.Job
27
+ import kotlinx.coroutines.async
25
28
import kotlinx.coroutines.delay
26
29
import kotlinx.coroutines.isActive
27
30
import kotlinx.coroutines.launch
28
- import kotlinx.coroutines.runBlocking
29
- import kotlinx.coroutines.withTimeout
30
31
import software.amazon.awssdk.core.exception.SdkServiceException
31
32
import software.amazon.awssdk.core.util.DefaultSdkAutoConstructList
32
33
import software.amazon.awssdk.services.codewhisperer.model.CodeWhispererException
@@ -43,6 +44,7 @@ import software.amazon.awssdk.services.codewhispererruntime.model.ThrottlingExce
43
44
import software.aws.toolkits.core.utils.debug
44
45
import software.aws.toolkits.core.utils.getLogger
45
46
import software.aws.toolkits.core.utils.info
47
+ import software.aws.toolkits.core.utils.warn
46
48
import software.aws.toolkits.jetbrains.core.coroutines.disposableCoroutineScope
47
49
import software.aws.toolkits.jetbrains.core.coroutines.projectCoroutineScope
48
50
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnection
@@ -76,9 +78,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhisperer
76
78
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.getTelemetryOptOutPreference
77
79
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.notifyErrorCodeWhispererUsageLimit
78
80
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.promptReAuth
79
- import software.aws.toolkits.jetbrains.services.codewhisperer.util.CrossFileStrategy
80
81
import software.aws.toolkits.jetbrains.services.codewhisperer.util.FileContextProvider
81
- import software.aws.toolkits.jetbrains.services.codewhisperer.util.UtgStrategy
82
82
import software.aws.toolkits.jetbrains.utils.isInjectedText
83
83
import software.aws.toolkits.jetbrains.utils.isQExpired
84
84
import software.aws.toolkits.jetbrains.utils.isRunningOnCWNotSupportedRemoteBackend
@@ -91,7 +91,7 @@ import software.aws.toolkits.telemetry.CodewhispererTriggerType
91
91
import java.util.concurrent.TimeUnit
92
92
93
93
@Service
94
- class CodeWhispererService : Disposable {
94
+ class CodeWhispererService ( private val cs : CoroutineScope ) : Disposable {
95
95
private val codeInsightSettingsFacade = CodeInsightsSettingsFacade ()
96
96
private var refreshFailure: Int = 0
97
97
@@ -184,7 +184,7 @@ class CodeWhispererService : Disposable {
184
184
invokeCodeWhispererInBackground(requestContext)
185
185
}
186
186
187
- private fun invokeCodeWhispererInBackground (requestContext : RequestContext ) {
187
+ internal fun invokeCodeWhispererInBackground (requestContext : RequestContext ): Job {
188
188
val popup = CodeWhispererPopupManager .getInstance().initPopup()
189
189
Disposer .register(popup) { CodeWhispererInvocationStatus .getInstance().finishInvocation() }
190
190
@@ -198,15 +198,16 @@ class CodeWhispererService : Disposable {
198
198
var states: InvocationContext ? = null
199
199
var lastRecommendationIndex = - 1
200
200
201
- val responseIterable = CodeWhispererClientAdaptor .getInstance(requestContext.project).generateCompletionsPaginator(
202
- buildCodeWhispererRequest(
203
- requestContext.fileContextInfo,
204
- requestContext.supplementalContext,
205
- requestContext.customizationArn
206
- )
207
- )
208
- coroutineScope.launch {
201
+ val job = coroutineScope.launch {
209
202
try {
203
+ val responseIterable = CodeWhispererClientAdaptor .getInstance(requestContext.project).generateCompletionsPaginator(
204
+ buildCodeWhispererRequest(
205
+ requestContext.fileContextInfo,
206
+ requestContext.awaitSupplementalContext(),
207
+ requestContext.customizationArn
208
+ )
209
+ )
210
+
210
211
var startTime = System .nanoTime()
211
212
requestContext.latencyContext.codewhispererPreprocessingEnd = System .nanoTime()
212
213
requestContext.latencyContext.paginationAllCompletionsStart = System .nanoTime()
@@ -413,6 +414,8 @@ class CodeWhispererService : Disposable {
413
414
CodeWhispererInvocationStatus .getInstance().setInvocationComplete()
414
415
}
415
416
}
417
+
418
+ return job
416
419
}
417
420
418
421
@RequiresEdt
@@ -621,29 +624,12 @@ class CodeWhispererService : Disposable {
621
624
622
625
// the upper bound for supplemental context duration is 50ms
623
626
// 2. supplemental context
624
- val startFetchingTimestamp = System .currentTimeMillis()
625
- val isTstFile = FileContextProvider .getInstance(project).isTestFile(psiFile)
626
- val supplementalContext = runBlocking {
627
+ val supplementalContext = cs.async {
627
628
try {
628
- withTimeout(SUPPLEMENTAL_CONTEXT_TIMEOUT ) {
629
- FileContextProvider .getInstance(project).extractSupplementalFileContext(psiFile, fileContext)
630
- }
629
+ FileContextProvider .getInstance(project).extractSupplementalFileContext(psiFile, fileContext, timeout = SUPPLEMENTAL_CONTEXT_TIMEOUT )
631
630
} catch (e: Exception ) {
632
- if (e is TimeoutCancellationException ) {
633
- LOG .debug {
634
- " Supplemental context fetch timed out in ${System .currentTimeMillis() - startFetchingTimestamp} ms"
635
- }
636
- SupplementalContextInfo (
637
- isUtg = isTstFile,
638
- contents = emptyList(),
639
- latency = System .currentTimeMillis() - startFetchingTimestamp,
640
- targetFileName = fileContext.filename,
641
- strategy = if (isTstFile) UtgStrategy .Empty else CrossFileStrategy .Empty
642
- )
643
- } else {
644
- LOG .debug { " Run into unexpected error when fetching supplemental context, error: ${e.message} " }
645
- null
646
- }
631
+ LOG .warn { " Run into unexpected error when fetching supplemental context, error: ${e.message} " }
632
+ null
647
633
}
648
634
}
649
635
@@ -825,11 +811,31 @@ data class RequestContext(
825
811
val triggerTypeInfo : TriggerTypeInfo ,
826
812
val caretPosition : CaretPosition ,
827
813
val fileContextInfo : FileContextInfo ,
828
- val supplementalContext : SupplementalContextInfo ? ,
814
+ private val supplementalContextDeferred : Deferred < SupplementalContextInfo ?> ,
829
815
val connection : ToolkitConnection ? ,
830
816
val latencyContext : LatencyContext ,
831
817
val customizationArn : String?
832
- )
818
+ ) {
819
+ // TODO: should make the entire getRequestContext() suspend function instead of making supplemental context only
820
+ var supplementalContext: SupplementalContextInfo ? = null
821
+ private set
822
+ get() = when (field) {
823
+ null -> {
824
+ if (! supplementalContextDeferred.isCompleted) {
825
+ error(" attempt to access supplemental context before awaiting the deferred" )
826
+ } else {
827
+ null
828
+ }
829
+ }
830
+
831
+ else -> field
832
+ }
833
+
834
+ suspend fun awaitSupplementalContext (): SupplementalContextInfo ? {
835
+ supplementalContext = supplementalContextDeferred.await()
836
+ return supplementalContext
837
+ }
838
+ }
833
839
834
840
data class ResponseContext (
835
841
val sessionId : String ,
0 commit comments