Skip to content

Commit 91cd6e3

Browse files
authored
Merge master into feature/hybridChat
2 parents e0dbbac + 49aa5c7 commit 91cd6e3

File tree

16 files changed

+256
-104
lines changed

16 files changed

+256
-104
lines changed

packages/amazonq/src/lsp/activation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import vscode from 'vscode'
77
import { startLanguageServer } from './client'
88
import { AmazonQLspInstaller } from './lspInstaller'
9-
import { lspSetupStage, ToolkitError } from 'aws-core-vscode/shared'
9+
import { lspSetupStage, ToolkitError, messages } from 'aws-core-vscode/shared'
1010

1111
export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
1212
try {
@@ -16,6 +16,6 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
1616
})
1717
} catch (err) {
1818
const e = err as ToolkitError
19-
void vscode.window.showInformationMessage(`Unable to launch amazonq language server: ${e.message}`)
19+
void messages.showViewLogsMessage(`Failed to launch Amazon Q language server: ${e.message}`)
2020
}
2121
}

packages/amazonq/src/lsp/chat/webviewProvider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export class AmazonQChatViewProvider implements WebviewViewProvider {
3939
) {
4040
this.webview = webviewView.webview
4141

42-
const lspDir = Uri.parse(LanguageServerResolver.defaultDir)
42+
const lspDir = Uri.parse(LanguageServerResolver.defaultDir())
4343
const dist = Uri.joinPath(globals.context.extensionUri, 'dist')
4444
webviewView.webview.options = {
4545
enableScripts: true,
@@ -68,7 +68,7 @@ export class AmazonQChatViewProvider implements WebviewViewProvider {
6868
const isSM = isSageMaker('SMAI')
6969
const isSMUS = isSageMaker('SMUS')
7070
const disabledCommands = isSM ? `['/dev', '/transform', '/test', '/review', '/doc']` : '[]'
71-
const disclaimerAcknowledged = AmazonQPromptSettings.instance.isPromptEnabled('amazonQChatDisclaimer')
71+
const disclaimerAcknowledged = !AmazonQPromptSettings.instance.isPromptEnabled('amazonQChatDisclaimer')
7272
const welcomeCount = globals.globalState.tryGet('aws.amazonq.welcomeChatShowCount', Number, 0)
7373

7474
// only show profile card when the two conditions

packages/amazonq/src/lsp/client.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,21 @@ import { InlineCompletionManager } from '../app/inline/completion'
1111
import { AmazonQLspAuth, encryptionKey, notificationTypes } from './auth'
1212
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
1313
import { ConnectionMetadata } from '@aws/language-server-runtimes/protocol'
14-
import { Settings, oidcClientName, createServerOptions, globals, Experiments, Commands } from 'aws-core-vscode/shared'
14+
import {
15+
Settings,
16+
oidcClientName,
17+
createServerOptions,
18+
globals,
19+
Experiments,
20+
Commands,
21+
validateNodeExe,
22+
getLogger,
23+
} from 'aws-core-vscode/shared'
1524
import { activate } from './chat/activation'
1625
import { AmazonQResourcePaths } from './lspInstaller'
1726

1827
const localize = nls.loadMessageBundle()
28+
const logger = getLogger('amazonqLsp.lspClient')
1929

2030
export async function startLanguageServer(
2131
extensionContext: vscode.ExtensionContext,
@@ -25,24 +35,27 @@ export async function startLanguageServer(
2535

2636
const serverModule = resourcePaths.lsp
2737

38+
const argv = [
39+
'--nolazy',
40+
'--preserve-symlinks',
41+
'--stdio',
42+
'--pre-init-encryption',
43+
'--set-credentials-encryption-key',
44+
]
2845
const serverOptions = createServerOptions({
2946
encryptionKey,
3047
executable: resourcePaths.node,
3148
serverModule,
32-
execArgv: [
33-
'--nolazy',
34-
'--preserve-symlinks',
35-
'--stdio',
36-
'--pre-init-encryption',
37-
'--set-credentials-encryption-key',
38-
],
49+
execArgv: argv,
3950
})
4051

4152
const documentSelector = [{ scheme: 'file', language: '*' }]
4253

4354
const clientId = 'amazonq'
4455
const traceServerEnabled = Settings.instance.isSet(`${clientId}.trace.server`)
4556

57+
await validateNodeExe(resourcePaths.node, resourcePaths.lsp, argv, logger)
58+
4659
// Options to control the language client
4760
const clientOptions: LanguageClientOptions = {
4861
// Register the server for json documents

packages/amazonq/test/unit/amazonq/lsp/lspClient.test.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
*/
55
import * as sinon from 'sinon'
66
import assert from 'assert'
7-
import { globals } from 'aws-core-vscode/shared'
8-
import { LspClient } from 'aws-core-vscode/amazonq'
7+
import { globals, getNodeExecutableName } from 'aws-core-vscode/shared'
8+
import { LspClient, lspClient as lspClientModule } from 'aws-core-vscode/amazonq'
99

1010
describe('Amazon Q LSP client', function () {
1111
let lspClient: LspClient
@@ -50,6 +50,22 @@ describe('Amazon Q LSP client', function () {
5050
assert.ok(!encryptedSample.includes('hello'))
5151
})
5252

53+
it('validates node executable + lsp bundle', async () => {
54+
await assert.rejects(async () => {
55+
await lspClientModule.activate(globals.context, {
56+
// Mimic the `LspResolution<ResourcePaths>` type.
57+
node: 'node.bogus.exe',
58+
lsp: 'fake/lsp.js',
59+
})
60+
}, /.*failed to run basic .*node.*exitcode.*node\.bogus\.exe.*/)
61+
await assert.rejects(async () => {
62+
await lspClientModule.activate(globals.context, {
63+
node: getNodeExecutableName(),
64+
lsp: 'fake/lsp.js',
65+
})
66+
}, /.*failed to run .*exitcode.*node.*lsp\.js/)
67+
})
68+
5369
afterEach(() => {
5470
sinon.restore()
5571
})

packages/core/src/amazonq/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export {
1717
} from './onboardingPage/walkthrough'
1818
export { LspController } from './lsp/lspController'
1919
export { LspClient } from './lsp/lspClient'
20+
export * as lspClient from './lsp/lspClient'
2021
export { api } from './extApi'
2122
export { AmazonQChatViewProvider } from './webview/webView'
2223
export { amazonQHelpUrl } from '../shared/constants'

packages/core/src/amazonq/lsp/lspClient.ts

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import * as jose from 'jose'
1515

1616
import { Disposable, ExtensionContext } from 'vscode'
1717

18-
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient'
18+
import { LanguageClient, LanguageClientOptions } from 'vscode-languageclient'
1919
import {
2020
BuildIndexRequestPayload,
2121
BuildIndexRequestType,
@@ -39,12 +39,13 @@ import { fs } from '../../shared/fs/fs'
3939
import { getLogger } from '../../shared/logger/logger'
4040
import globals from '../../shared/extensionGlobals'
4141
import { ResourcePaths } from '../../shared/lsp/types'
42-
import { createServerOptions } from '../../shared/lsp/utils/platform'
42+
import { createServerOptions, validateNodeExe } from '../../shared/lsp/utils/platform'
4343
import { waitUntil } from '../../shared/utilities/timeoutUtils'
4444

4545
const localize = nls.loadMessageBundle()
4646

4747
const key = crypto.randomBytes(32)
48+
const logger = getLogger('amazonqWorkspaceLsp')
4849

4950
/**
5051
* LspClient manages the API call between VS Code extension and LSP server
@@ -80,7 +81,7 @@ export class LspClient {
8081
const resp = await this.client?.sendRequest(BuildIndexRequestType, encryptedRequest)
8182
return resp
8283
} catch (e) {
83-
getLogger().error(`LspClient: buildIndex error: ${e}`)
84+
logger.error(`buildIndex error: ${e}`)
8485
return undefined
8586
}
8687
}
@@ -95,7 +96,7 @@ export class LspClient {
9596
const resp = await this.client?.sendRequest(QueryVectorIndexRequestType, encryptedRequest)
9697
return resp
9798
} catch (e) {
98-
getLogger().error(`LspClient: queryVectorIndex error: ${e}`)
99+
logger.error(`queryVectorIndex error: ${e}`)
99100
return []
100101
}
101102
}
@@ -111,7 +112,7 @@ export class LspClient {
111112
const resp: any = await this.client?.sendRequest(QueryInlineProjectContextRequestType, encrypted)
112113
return resp
113114
} catch (e) {
114-
getLogger().error(`LspClient: queryInlineProjectContext error: ${e}`)
115+
logger.error(`queryInlineProjectContext error: ${e}`)
115116
throw e
116117
}
117118
}
@@ -132,7 +133,7 @@ export class LspClient {
132133
const resp = await this.client?.sendRequest(UpdateIndexV2RequestType, encryptedRequest)
133134
return resp
134135
} catch (e) {
135-
getLogger().error(`LspClient: updateIndex error: ${e}`)
136+
logger.error(`updateIndex error: ${e}`)
136137
return undefined
137138
}
138139
}
@@ -144,7 +145,7 @@ export class LspClient {
144145
const resp: any = await this.client?.sendRequest(QueryRepomapIndexRequestType, await this.encrypt(request))
145146
return resp
146147
} catch (e) {
147-
getLogger().error(`LspClient: QueryRepomapIndex error: ${e}`)
148+
logger.error(`QueryRepomapIndex error: ${e}`)
148149
throw e
149150
}
150151
}
@@ -157,7 +158,7 @@ export class LspClient {
157158
)
158159
return resp
159160
} catch (e) {
160-
getLogger().error(`LspClient: queryInlineProjectContext error: ${e}`)
161+
logger.error(`queryInlineProjectContext error: ${e}`)
161162
throw e
162163
}
163164
}
@@ -174,7 +175,7 @@ export class LspClient {
174175
)
175176
return resp
176177
} catch (e) {
177-
getLogger().error(`LspClient: getContextCommandItems error: ${e}`)
178+
logger.error(`getContextCommandItems error: ${e}`)
178179
throw e
179180
}
180181
}
@@ -190,7 +191,7 @@ export class LspClient {
190191
)
191192
return resp || []
192193
} catch (e) {
193-
getLogger().error(`LspClient: getContextCommandPrompt error: ${e}`)
194+
logger.error(`getContextCommandPrompt error: ${e}`)
194195
throw e
195196
}
196197
}
@@ -204,7 +205,7 @@ export class LspClient {
204205
)
205206
return resp
206207
} catch (e) {
207-
getLogger().error(`LspClient: getIndexSequenceNumber error: ${e}`)
208+
logger.error(`getIndexSequenceNumber error: ${e}`)
208209
throw e
209210
}
210211
}
@@ -222,20 +223,20 @@ export class LspClient {
222223
)
223224
}
224225
}
226+
225227
/**
226-
* Activates the language server, this will start LSP server running over IPC protocol.
227-
* It will create a output channel named Amazon Q Language Server.
228-
* This function assumes the LSP server has already been downloaded.
228+
* Activates the language server (assumes the LSP server has already been downloaded):
229+
* 1. start LSP server running over IPC protocol.
230+
* 2. create a output channel named Amazon Q Language Server.
229231
*/
230232
export async function activate(extensionContext: ExtensionContext, resourcePaths: ResourcePaths) {
231-
LspClient.instance
233+
LspClient.instance // Tickle the singleton... :/
232234
const toDispose = extensionContext.subscriptions
233235

234236
let rangeFormatting: Disposable | undefined
235237
// The debug options for the server
236238
// --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging
237239
const debugOptions = { execArgv: ['--nolazy', '--preserve-symlinks', '--stdio'] }
238-
239240
const workerThreads = CodeWhispererSettings.instance.getIndexWorkerThreads()
240241
const gpu = CodeWhispererSettings.instance.isLocalIndexGPUEnabled()
241242

@@ -252,22 +253,18 @@ export async function activate(extensionContext: ExtensionContext, resourcePaths
252253

253254
const serverModule = resourcePaths.lsp
254255

255-
// If the extension is launch in debug mode the debug server options are use
256-
// Otherwise the run options are used
257-
let serverOptions: ServerOptions = {
258-
run: { module: serverModule, transport: TransportKind.ipc },
259-
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions },
260-
}
261-
262-
serverOptions = createServerOptions({
256+
const serverOptions = createServerOptions({
263257
encryptionKey: key,
264258
executable: resourcePaths.node,
265259
serverModule,
260+
// TODO(jmkeyes): we always use the debug options...?
266261
execArgv: debugOptions.execArgv,
267262
})
268263

269264
const documentSelector = [{ scheme: 'file', language: '*' }]
270265

266+
await validateNodeExe(resourcePaths.node, resourcePaths.lsp, debugOptions.execArgv, logger)
267+
271268
// Options to control the language client
272269
const clientOptions: LanguageClientOptions = {
273270
// Register the server for json documents
@@ -359,10 +356,15 @@ export async function activate(extensionContext: ExtensionContext, resourcePaths
359356
})
360357
)
361358

362-
return LspClient.instance.client.onReady().then(() => {
363-
const disposableFunc = { dispose: () => rangeFormatting?.dispose() as void }
364-
toDispose.push(disposableFunc)
365-
})
359+
return LspClient.instance.client.onReady().then(
360+
() => {
361+
const disposableFunc = { dispose: () => rangeFormatting?.dispose() as void }
362+
toDispose.push(disposableFunc)
363+
},
364+
(reason) => {
365+
logger.error('client.onReady() failed: %O', reason)
366+
}
367+
)
366368
}
367369

368370
export async function deactivate(): Promise<any> {

0 commit comments

Comments
 (0)