@@ -42,12 +42,21 @@ import kotlinx.coroutines.channels.Channel
4242import  kotlinx.coroutines.flow.receiveAsFlow 
4343import  kotlinx.coroutines.future.await 
4444import  kotlinx.coroutines.launch 
45+ import  kotlinx.coroutines.withContext 
4546import  migration.software.aws.toolkits.jetbrains.services.codewhisperer.explorer.CodeWhispererExplorerActionManager 
47+ import  org.eclipse.lsp4j.jsonrpc.ResponseErrorException 
4648import  org.eclipse.lsp4j.jsonrpc.messages.Either 
49+ import  software.amazon.awssdk.services.ssooidc.model.InvalidGrantException 
4750import  software.aws.toolkits.core.utils.debug 
4851import  software.aws.toolkits.core.utils.getLogger 
52+ import  software.aws.toolkits.jetbrains.core.coroutines.getCoroutineBgContext 
53+ import  software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection 
54+ import  software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager 
55+ import  software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection 
56+ import  software.aws.toolkits.jetbrains.core.credentials.sso.bearer.BearerTokenProvider 
4957import  software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService 
5058import  software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager 
59+ import  software.aws.toolkits.jetbrains.services.codewhisperer.explorer.isCodeWhispererEnabled 
5160import  software.aws.toolkits.jetbrains.services.codewhisperer.importadder.CodeWhispererImportAdder 
5261import  software.aws.toolkits.jetbrains.services.codewhisperer.model.InlineCompletionItemContext 
5362import  software.aws.toolkits.jetbrains.services.codewhisperer.model.InlineCompletionSessionContext 
@@ -59,7 +68,9 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispe
5968import  software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService 
6069import  software.aws.toolkits.jetbrains.services.codewhisperer.toolwindow.CodeWhispererCodeReferenceManager 
6170import  software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants 
71+ import  software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil 
6272import  software.aws.toolkits.jetbrains.utils.isQConnected 
73+ import  software.aws.toolkits.jetbrains.utils.isQExpired 
6374import  software.aws.toolkits.resources.message 
6475import  software.aws.toolkits.telemetry.CodewhispererTriggerType 
6576import  java.awt.Dimension 
@@ -391,8 +402,23 @@ class QInlineCompletionProvider(private val cs: CoroutineScope) : InlineCompleti
391402
392403    override  suspend  fun  getSuggestion (request :  InlineCompletionRequest ): InlineCompletionSuggestion  {
393404        val  editor =  request.editor
394-         val  document =  editor.document
395405        val  project =  editor.project ? :  return  InlineCompletionSuggestion .Empty 
406+         if  (! isCodeWhispererEnabled(project)) return  InlineCompletionSuggestion .Empty 
407+ 
408+         //  try to refresh automatically if possible, otherwise ask user to login again
409+         if  (isQExpired(project)) {
410+             //  consider changing to only running once a ~minute since this is relatively expensive
411+             //  say the connection is un-refreshable if refresh fails for 3 times
412+             val  shouldReauth =  withContext(getCoroutineBgContext()) {
413+                 CodeWhispererUtil .promptReAuth(project)
414+             }
415+ 
416+             if  (shouldReauth) {
417+                 return  InlineCompletionSuggestion .Empty 
418+             }
419+         }
420+ 
421+         val  document =  editor.document
396422        val  handler =  InlineCompletion .getHandlerOrNull(editor) ? :  return  InlineCompletionSuggestion .Empty 
397423        val  session =  InlineCompletionSession .getOrNull(editor) ? :  return  InlineCompletionSuggestion .Empty 
398424        val  triggerSessionId =  triggerSessionId++ 
@@ -443,31 +469,28 @@ class QInlineCompletionProvider(private val cs: CoroutineScope) : InlineCompleti
443469        }
444470
445471        try  {
446-             //  Launch coroutine for background pagination progress
447-             cs.launch {
448-                 var  nextToken:  Either <String , Int >?  =  null 
449-                 do  {
450-                     nextToken =  startPaginationInBackground(
451-                         project,
452-                         editor,
453-                         triggerTypeInfo,
454-                         triggerSessionId,
455-                         nextToken,
456-                         sessionContext,
457-                     )
458-                 } while  (nextToken !=  null  &&  ! nextToken.left.isNullOrEmpty())
472+             var  nextToken:  Either <String , Int >?  =  null 
473+             do  {
474+                 nextToken =  startPaginationInBackground(
475+                     project,
476+                     editor,
477+                     triggerTypeInfo,
478+                     triggerSessionId,
479+                     nextToken,
480+                     sessionContext,
481+                 )
482+             } while  (nextToken !=  null  &&  ! nextToken.left.isNullOrEmpty())
459483
460-                 //  closing all channels since pagination for this session has finished
484+             //  closing all channels since pagination for this session has finished
485+             logInline(triggerSessionId) {
486+                 " Pagination finished, closing all channels" 
487+             }
488+             sessionContext.itemContexts.forEach {
489+                 it.channel.close()
490+             }
491+             if  (session.context.isDisposed) {
461492                logInline(triggerSessionId) {
462-                     " Pagination finished, closing all channels" 
463-                 }
464-                 sessionContext.itemContexts.forEach {
465-                     it.channel.close()
466-                 }
467-                 if  (session.context.isDisposed) {
468-                     logInline(triggerSessionId) {
469-                         " Current display session already disposed by a new trigger before pagination finishes, exiting" 
470-                     }
493+                     " Current display session already disposed by a new trigger before pagination finishes, exiting" 
471494                }
472495            }
473496
@@ -572,6 +595,27 @@ class QInlineCompletionProvider(private val cs: CoroutineScope) : InlineCompleti
572595            logInline(triggerSessionId, e) {
573596                " Error during pagination" 
574597            }
598+             if  (e is  ResponseErrorException ) {
599+                 //  convoluted but lines up with "The bearer token included in the request is invalid"
600+                 //  https://github.com/aws/language-servers/blob/1f3e93024eeb22186a34f0bd560f8d552f517300/server/aws-lsp-codewhisperer/src/language-server/chat/utils.ts#L22-L23
601+                 //  error data is nullable
602+                 if  (e.responseError.data?.toString()?.contains(" E_AMAZON_Q_CONNECTION_EXPIRED"  ) ==  true ) {
603+                     //  kill the session if the connection is expired
604+                     val  connection =  ToolkitConnectionManager 
605+                         .getInstance(project)
606+                         .activeConnectionForFeature(QConnection .getInstance()) as ?  AwsBearerTokenConnection 
607+                     val  tokenProvider =  connection?.let  { it.getConnectionSettings().tokenProvider.delegate as ?  BearerTokenProvider  }
608+                     tokenProvider?.let  {
609+                         //  TODO: fragile
610+                         try  {
611+                             it.refresh()
612+                         } catch  (_:  InvalidGrantException ){
613+                             it.invalidate()
614+                             CodeWhispererUtil .reconnectCodeWhisperer(project)
615+                         }
616+                     }
617+                 }
618+             }
575619            return  null 
576620        }
577621    }
@@ -591,6 +635,7 @@ class QInlineCompletionProvider(private val cs: CoroutineScope) : InlineCompleti
591635        val  editor =  request.editor
592636        val  project =  editor.project ? :  return  false 
593637
638+         //  qExpired case handled in completion handler
594639        if  (! isQConnected(project)) return  false 
595640        if  (! CodeWhispererExplorerActionManager .getInstance().isAutoEnabled() &&  event.isManualCall()) return  false 
596641        if  (QRegionProfileManager .getInstance().hasValidConnectionButNoActiveProfile(project)) return  false 
0 commit comments