66import vscode , { env , version } from 'vscode'
77import * as nls from 'vscode-nls'
88import * as crypto from 'crypto'
9+ import * as jose from 'jose'
910import { LanguageClient , LanguageClientOptions } from 'vscode-languageclient'
1011import { InlineCompletionManager } from '../app/inline/completion'
11- import { AmazonQLspAuth , encryptionKey , notificationTypes } from './auth'
1212import { AuthUtil } from 'aws-core-vscode/codewhisperer'
13- import { ConnectionMetadata } from '@aws/language-server-runtimes/protocol'
14- import { Settings , oidcClientName , createServerOptions , globals , Experiments , Commands } from 'aws-core-vscode/shared'
13+ import {
14+ ConnectionMetadata ,
15+ GetSsoTokenProgress ,
16+ GetSsoTokenProgressToken ,
17+ GetSsoTokenProgressType ,
18+ MessageActionItem ,
19+ ShowDocumentParams ,
20+ ShowDocumentRequest ,
21+ ShowDocumentResult ,
22+ ShowMessageRequest ,
23+ ShowMessageRequestParams ,
24+ } from '@aws/language-server-runtimes/protocol'
25+ import {
26+ Settings ,
27+ oidcClientName ,
28+ createServerOptions ,
29+ globals ,
30+ Experiments ,
31+ Commands ,
32+ openUrl ,
33+ getLogger ,
34+ } from 'aws-core-vscode/shared'
1535import { activate } from './chat/activation'
1636import { AmazonQResourcePaths } from './lspInstaller'
37+ import { auth2 } from 'aws-core-vscode/auth'
1738
1839const localize = nls . loadMessageBundle ( )
1940
41+ export const clientId = 'amazonq'
42+ export const clientName = oidcClientName ( )
43+ export const encryptionKey = crypto . randomBytes ( 32 )
44+
2045export async function startLanguageServer (
2146 extensionContext : vscode . ExtensionContext ,
2247 resourcePaths : AmazonQResourcePaths
23- ) {
48+ ) : Promise < LanguageClient > {
2449 const toDispose = extensionContext . subscriptions
2550
2651 const serverModule = resourcePaths . lsp
@@ -39,8 +64,6 @@ export async function startLanguageServer(
3964 } )
4065
4166 const documentSelector = [ { scheme : 'file' , language : '*' } ]
42-
43- const clientId = 'amazonq'
4467 const traceServerEnabled = Settings . instance . isSet ( `${ clientId } .trace.server` )
4568
4669 // Options to control the language client
@@ -80,57 +103,89 @@ export async function startLanguageServer(
80103 } ) ,
81104 }
82105
83- const client = new LanguageClient (
84- clientId ,
85- localize ( 'amazonq.server.name' , 'Amazon Q Language Server' ) ,
86- serverOptions ,
87- clientOptions
88- )
106+ const lspName = localize ( 'amazonq.server.name' , 'Amazon Q Language Server' )
107+ const client = new LanguageClient ( clientId , lspName , serverOptions , clientOptions )
89108
90109 const disposable = client . start ( )
91110 toDispose . push ( disposable )
92111
93- const auth = new AmazonQLspAuth ( client )
112+ await client . onReady ( )
94113
95- return client . onReady ( ) . then ( async ( ) => {
96- // Request handler for when the server wants to know about the clients auth connnection. Must be registered before the initial auth init call
97- client . onRequest < ConnectionMetadata , Error > ( notificationTypes . getConnectionMetadata . method , ( ) => {
98- return {
99- sso : {
100- startUrl : AuthUtil . instance . auth . startUrl ,
101- } ,
102- }
103- } )
104- await auth . refreshConnection ( )
105-
106- if ( Experiments . instance . get ( 'amazonqLSPInline' , false ) ) {
107- const inlineManager = new InlineCompletionManager ( client )
108- inlineManager . registerInlineCompletion ( )
109- toDispose . push (
110- inlineManager ,
111- Commands . register ( { id : 'aws.amazonq.invokeInlineCompletion' , autoconnect : true } , async ( ) => {
112- await vscode . commands . executeCommand ( 'editor.action.inlineSuggest.trigger' )
113- } ) ,
114- vscode . workspace . onDidCloseTextDocument ( async ( ) => {
115- await vscode . commands . executeCommand ( 'aws.amazonq.rejectCodeSuggestion' )
116- } )
117- )
114+ // Request handler for when the server wants to know about the clients auth connnection. Must be registered before the initial auth init call
115+ client . onRequest < ConnectionMetadata , Error > ( auth2 . notificationTypes . getConnectionMetadata . method , ( ) => {
116+ return {
117+ sso : {
118+ startUrl : AuthUtil . instance . connection ?. startUrl ,
119+ } ,
118120 }
121+ } )
119122
120- if ( Experiments . instance . get ( 'amazonqChatLSP' , false ) ) {
121- activate ( client , encryptionKey , resourcePaths . ui )
123+ client . onRequest < ShowDocumentResult , Error > ( ShowDocumentRequest . method , async ( params : ShowDocumentParams ) => {
124+ try {
125+ return { success : await openUrl ( vscode . Uri . parse ( params . uri ) , lspName ) }
126+ } catch ( err : any ) {
127+ getLogger ( ) . error ( `Failed to open document for LSP: ${ lspName } , error: %s` , err )
128+ return { success : false }
122129 }
130+ } )
131+
132+ client . onRequest < MessageActionItem | null , Error > (
133+ ShowMessageRequest . method ,
134+ async ( params : ShowMessageRequestParams ) => {
135+ const actions = params . actions ?. map ( ( a ) => a . title ) ?? [ ]
136+ const response = await vscode . window . showInformationMessage ( params . message , { modal : true } , ...actions )
137+ return params . actions ?. find ( ( a ) => a . title === response ) ?? ( undefined as unknown as null )
138+ }
139+ )
123140
124- const refreshInterval = auth . startTokenRefreshInterval ( )
141+ let promise : Promise < void > | undefined
142+ let resolver : ( ) => void = ( ) => { }
143+ client . onProgress ( GetSsoTokenProgressType , GetSsoTokenProgressToken , async ( partialResult : GetSsoTokenProgress ) => {
144+ const decryptedKey = await jose . compactDecrypt ( partialResult as unknown as string , encryptionKey )
145+ const val : GetSsoTokenProgress = JSON . parse ( decryptedKey . plaintext . toString ( ) )
125146
147+ if ( val . state === 'InProgress' ) {
148+ if ( promise ) {
149+ resolver ( )
150+ }
151+ promise = new Promise < void > ( ( resolve ) => {
152+ resolver = resolve
153+ } )
154+ } else {
155+ resolver ( )
156+ promise = undefined
157+ return
158+ }
159+
160+ void vscode . window . withProgress (
161+ {
162+ cancellable : true ,
163+ location : vscode . ProgressLocation . Notification ,
164+ title : val . message ,
165+ } ,
166+ async ( _ ) => {
167+ await promise
168+ }
169+ )
170+ } )
171+
172+ if ( Experiments . instance . get ( 'amazonqLSPInline' , false ) ) {
173+ const inlineManager = new InlineCompletionManager ( client )
174+ inlineManager . registerInlineCompletion ( )
126175 toDispose . push (
127- AuthUtil . instance . auth . onDidChangeActiveConnection ( async ( ) => {
128- await auth . refreshConnection ( )
176+ inlineManager ,
177+ Commands . register ( { id : 'aws.amazonq.invokeInlineCompletion' , autoconnect : true } , async ( ) => {
178+ await vscode . commands . executeCommand ( 'editor.action.inlineSuggest.trigger' )
129179 } ) ,
130- AuthUtil . instance . auth . onDidDeleteConnection ( async ( ) => {
131- client . sendNotification ( notificationTypes . deleteBearerToken . method )
132- } ) ,
133- { dispose : ( ) => clearInterval ( refreshInterval ) }
180+ vscode . workspace . onDidCloseTextDocument ( async ( ) => {
181+ await vscode . commands . executeCommand ( 'aws.amazonq.rejectCodeSuggestion' )
182+ } )
134183 )
135- } )
184+ }
185+
186+ if ( Experiments . instance . get ( 'amazonqChatLSP' , false ) ) {
187+ activate ( client , encryptionKey , resourcePaths . ui )
188+ }
189+
190+ return client
136191}
0 commit comments