@@ -42,10 +42,18 @@ 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 
5159import  software.aws.toolkits.jetbrains.services.codewhisperer.importadder.CodeWhispererImportAdder 
@@ -59,8 +67,10 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispe
5967import  software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService 
6068import  software.aws.toolkits.jetbrains.services.codewhisperer.toolwindow.CodeWhispererCodeReferenceManager 
6169import  software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants 
70+ import  software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil 
6271import  software.aws.toolkits.jetbrains.services.codewhisperer.util.getDocumentDiagnostics 
6372import  software.aws.toolkits.jetbrains.utils.isQConnected 
73+ import  software.aws.toolkits.jetbrains.utils.isQExpired 
6474import  software.aws.toolkits.resources.message 
6575import  software.aws.toolkits.telemetry.CodewhispererTriggerType 
6676import  java.awt.Dimension 
@@ -393,8 +403,22 @@ class QInlineCompletionProvider(private val cs: CoroutineScope) : InlineCompleti
393403
394404    override  suspend  fun  getSuggestion (request :  InlineCompletionRequest ): InlineCompletionSuggestion  {
395405        val  editor =  request.editor
396-         val  document =  editor.document
397406        val  project =  editor.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
398422        val  handler =  InlineCompletion .getHandlerOrNull(editor) ? :  return  InlineCompletionSuggestion .Empty 
399423        val  session =  InlineCompletionSession .getOrNull(editor) ? :  return  InlineCompletionSuggestion .Empty 
400424        val  triggerSessionId =  triggerSessionId++ 
@@ -446,31 +470,28 @@ class QInlineCompletionProvider(private val cs: CoroutineScope) : InlineCompleti
446470        }
447471
448472        try  {
449-             //  Launch coroutine for background pagination progress
450-             cs.launch {
451-                 var  nextToken:  Either <String , Int >?  =  null 
452-                 do  {
453-                     nextToken =  startPaginationInBackground(
454-                         project,
455-                         editor,
456-                         triggerTypeInfo,
457-                         triggerSessionId,
458-                         nextToken,
459-                         sessionContext,
460-                     )
461-                 } while  (nextToken !=  null  &&  ! nextToken.left.isNullOrEmpty())
473+             var  nextToken:  Either <String , Int >?  =  null 
474+             do  {
475+                 nextToken =  startPaginationInBackground(
476+                     project,
477+                     editor,
478+                     triggerTypeInfo,
479+                     triggerSessionId,
480+                     nextToken,
481+                     sessionContext,
482+                 )
483+             } while  (nextToken !=  null  &&  ! nextToken.left.isNullOrEmpty())
462484
463-                 //  closing all channels since pagination for this session has finished
485+             //  closing all channels since pagination for this session has finished
486+             logInline(triggerSessionId) {
487+                 " Pagination finished, closing all channels" 
488+             }
489+             sessionContext.itemContexts.forEach {
490+                 it.channel.close()
491+             }
492+             if  (session.context.isDisposed) {
464493                logInline(triggerSessionId) {
465-                     " Pagination finished, closing all channels" 
466-                 }
467-                 sessionContext.itemContexts.forEach {
468-                     it.channel.close()
469-                 }
470-                 if  (session.context.isDisposed) {
471-                     logInline(triggerSessionId) {
472-                         " Current display session already disposed by a new trigger before pagination finishes, exiting" 
473-                     }
494+                     " Current display session already disposed by a new trigger before pagination finishes, exiting" 
474495                }
475496            }
476497
@@ -575,6 +596,27 @@ class QInlineCompletionProvider(private val cs: CoroutineScope) : InlineCompleti
575596            logInline(triggerSessionId, e) {
576597                " Error during pagination" 
577598            }
599+             if  (e is  ResponseErrorException ) {
600+                 //  convoluted but lines up with "The bearer token included in the request is invalid"
601+                 //  https://github.com/aws/language-servers/blob/1f3e93024eeb22186a34f0bd560f8d552f517300/server/aws-lsp-codewhisperer/src/language-server/chat/utils.ts#L22-L23
602+                 //  error data is nullable
603+                 if  (e.responseError.data?.toString()?.contains(" E_AMAZON_Q_CONNECTION_EXPIRED"  ) ==  true ) {
604+                     //  kill the session if the connection is expired
605+                     val  connection =  ToolkitConnectionManager 
606+                         .getInstance(project)
607+                         .activeConnectionForFeature(QConnection .getInstance()) as ?  AwsBearerTokenConnection 
608+                     val  tokenProvider =  connection?.let  { it.getConnectionSettings().tokenProvider.delegate as ?  BearerTokenProvider  }
609+                     tokenProvider?.let  {
610+                         //  TODO: fragile
611+                         try  {
612+                             it.refresh()
613+                         } catch  (_:  InvalidGrantException ) {
614+                             it.invalidate()
615+                             CodeWhispererUtil .reconnectCodeWhisperer(project)
616+                         }
617+                     }
618+                 }
619+             }
578620            return  null 
579621        }
580622    }
@@ -594,6 +636,7 @@ class QInlineCompletionProvider(private val cs: CoroutineScope) : InlineCompleti
594636        val  editor =  request.editor
595637        val  project =  editor.project ? :  return  false 
596638
639+         //  qExpired case handled in completion handler
597640        if  (! isQConnected(project)) return  false 
598641        if  (QRegionProfileManager .getInstance().hasValidConnectionButNoActiveProfile(project)) return  false 
599642        if  (event.isManualCall()) return  true 
0 commit comments