Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions packages/amazonq/src/lsp/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,11 @@ import vscode from 'vscode'
import { startLanguageServer } from './client'
import { AmazonQLSPResolver } from './lspInstaller'
import { ToolkitError } from 'aws-core-vscode/shared'
import path from 'path'

export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
try {
const installResult = await new AmazonQLSPResolver().resolve()
const serverLocation =
installResult.location === 'override'
? installResult.assetDirectory
: path.join(installResult.assetDirectory, 'servers/aws-lsp-codewhisperer.js')
await startLanguageServer(ctx, serverLocation)
await startLanguageServer(ctx, installResult.executablePaths)
} catch (err) {
const e = err as ToolkitError
void vscode.window.showInformationMessage(`Unable to launch amazonq language server: ${e.message}`)
Expand Down
7 changes: 5 additions & 2 deletions packages/amazonq/src/lsp/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import { registerInlineCompletion } from '../inline/completion'
import { AmazonQLspAuth, notificationTypes, writeEncryptionInit } from './auth'
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
import { ConnectionMetadata } from '@aws/language-server-runtimes/protocol'
import { ExecutablePaths } from 'aws-core-vscode/shared'

const localize = nls.loadMessageBundle()

export function startLanguageServer(extensionContext: vscode.ExtensionContext, serverPath: string) {
export function startLanguageServer(extensionContext: vscode.ExtensionContext, executablePaths: ExecutablePaths) {
const toDispose = extensionContext.subscriptions

// The debug options for the server
Expand All @@ -30,14 +31,16 @@ export function startLanguageServer(extensionContext: vscode.ExtensionContext, s
],
}

const serverPath = executablePaths.lsp

// If the extension is launch in debug mode the debug server options are use
// Otherwise the run options are used
let serverOptions: ServerOptions = {
run: { module: serverPath, transport: TransportKind.ipc },
debug: { module: serverPath, transport: TransportKind.ipc, options: debugOptions },
}

const child = cp.spawn('node', [serverPath, ...debugOptions.execArgv])
const child = cp.spawn(executablePaths.node, [serverPath, ...debugOptions.execArgv])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we can find a way to use our ChildProcess module, then after #6304 we get instrumentation for free :)

writeEncryptionInit(child.stdin)

serverOptions = () => Promise.resolve(child)
Expand Down
27 changes: 24 additions & 3 deletions packages/amazonq/src/lsp/lspInstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,32 @@

import * as vscode from 'vscode'
import { Range } from 'semver'
import { ManifestResolver, LanguageServerResolver, LspResolver, LspResult } from 'aws-core-vscode/shared'
import {
ManifestResolver,
LanguageServerResolver,
LspResolver,
fs,
LspResolution,
getNodeExecutableName,
} from 'aws-core-vscode/shared'
import path from 'path'

const manifestURL = 'https://aws-toolkit-language-servers.amazonaws.com/codewhisperer/0/manifest.json'
export const supportedLspServerVersions = '^2.3.0'

export class AmazonQLSPResolver implements LspResolver {
async resolve(): Promise<LspResult> {
async resolve(): Promise<LspResolution> {
const overrideLocation = process.env.AWS_LANGUAGE_SERVER_OVERRIDE
if (overrideLocation) {
void vscode.window.showInformationMessage(`Using language server override location: ${overrideLocation}`)
return {
assetDirectory: overrideLocation,
location: 'override',
version: '0.0.0',
executablePaths: {
lsp: overrideLocation,
node: getNodeExecutableName(),
},
}
}

Expand All @@ -31,7 +43,16 @@ export class AmazonQLSPResolver implements LspResolver {
new Range(supportedLspServerVersions)
).resolve()

const nodePath = path.join(installationResult.assetDirectory, `servers/${getNodeExecutableName()}`)
await fs.chmod(nodePath, 0o755)

// TODO Cleanup old versions of language servers
return installationResult
return {
...installationResult,
executablePaths: {
lsp: path.join(installationResult.assetDirectory, 'servers/aws-lsp-codewhisperer.js'),
node: nodePath,
},
}
}
}
11 changes: 4 additions & 7 deletions packages/core/src/amazonq/lsp/lspClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
} from './types'
import { Writable } from 'stream'
import { CodeWhispererSettings } from '../../codewhisperer/util/codewhispererSettings'
import { fs, getLogger, globals } from '../../shared'
import { ExecutablePaths, fs, getLogger, globals } from '../../shared'

const localize = nls.loadMessageBundle()

Expand Down Expand Up @@ -172,7 +172,7 @@ export class LspClient {
* It will create a output channel named Amazon Q Language Server.
* This function assumes the LSP server has already been downloaded.
*/
export async function activate(extensionContext: ExtensionContext, serverModule: string) {
export async function activate(extensionContext: ExtensionContext, executablePaths: ExecutablePaths) {
LspClient.instance
const toDispose = extensionContext.subscriptions

Expand All @@ -195,12 +195,9 @@ export async function activate(extensionContext: ExtensionContext, serverModule:
delete process.env.Q_WORKER_THREADS
}

const nodename = process.platform === 'win32' ? 'node.exe' : 'node'
const serverModule = executablePaths.lsp

const child = spawn(extensionContext.asAbsolutePath(path.join('resources', nodename)), [
serverModule,
...debugOptions.execArgv,
])
const child = spawn(executablePaths.node, [serverModule, ...debugOptions.execArgv])
// share an encryption key using stdin
// follow same practice of DEXP LSP server
writeEncryptionInit(child.stdin)
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/amazonq/lsp/lspController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export class LspController {
setImmediate(async () => {
try {
const installResult = await new WorkspaceLSPResolver().resolve()
await activateLsp(context, path.join(installResult.assetDirectory, 'resources/qserver/lspServer.js'))
await activateLsp(context, installResult.executablePaths)
getLogger().info('LspController: LSP activated')
void LspController.instance.buildIndex(buildIndexConfig)
// log the LSP server CPU and Memory usage per 30 minutes.
Expand Down
23 changes: 20 additions & 3 deletions packages/core/src/amazonq/lsp/workspaceInstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { LspResolver, LspResult } from '../../shared/languageServer/types'
import path from 'path'
import { LspResolution, LspResolver } from '../../shared/languageServer/types'
import { ManifestResolver } from '../../shared/languageServer/manifestResolver'
import { LanguageServerResolver } from '../../shared/languageServer/lspResolver'
import { Range } from 'semver'
import { getNodeExecutableName } from '../../shared/languageServer/utils/platform'
import { fs } from '../../shared/fs/fs'

const manifestUrl = 'https://aws-toolkit-language-servers.amazonaws.com/q-context/manifest.json'
// this LSP client in Q extension is only going to work with these LSP server versions
const supportedLspServerVersions = '0.1.32'

export class WorkspaceLSPResolver implements LspResolver {
async resolve(): Promise<LspResult> {
async resolve(): Promise<LspResolution> {
const name = 'AmazonQ-Workspace'
const manifest = await new ManifestResolver(manifestUrl, name).resolve()
const installationResult = await new LanguageServerResolver(
Expand All @@ -22,7 +25,21 @@ export class WorkspaceLSPResolver implements LspResolver {
new Range(supportedLspServerVersions)
).resolve()

const nodeName =
process.platform === 'win32' ? getNodeExecutableName() : `node-${process.platform}-${process.arch}`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would the node-platfor-arch logic fit into getNodeExecutableName ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason this idea only exists for the workspace installer. I think the actual standard (which the actual codewhisperer language server uses) is just node.exe/node

const nodePath = path.join(installationResult.assetDirectory, nodeName)
await fs.chmod(nodePath, 0o755)

// TODO Cleanup old versions of language servers
return installationResult
return {
...installationResult,
executablePaths: {
lsp: path.join(
installationResult.assetDirectory,
`qserver-${process.platform}-${process.arch}/qserver/lspServer.js`
),
node: nodePath,
},
}
}
}
1 change: 1 addition & 0 deletions packages/core/src/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,4 @@ export * from './languageServer/manifestResolver'
export * from './languageServer/lspResolver'
export * from './languageServer/types'
export { default as request } from './request'
export * from './languageServer/utils/platform'
10 changes: 9 additions & 1 deletion packages/core/src/shared/languageServer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,16 @@ export interface LspResult {
assetDirectory: string
}

export interface ExecutablePaths {
lsp: string
node: string
}
export interface LspResolution extends LspResult {
executablePaths: ExecutablePaths
}

export interface LspResolver {
resolve(): Promise<LspResult>
resolve(): Promise<LspResolution>
}

export interface TargetContent {
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/shared/languageServer/utils/platform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

export function getNodeExecutableName(): string {
return process.platform === 'win32' ? 'node.exe' : 'node'
}
Loading