From b8bec08ca877f6ae0a195cc4764f09bce9efc535 Mon Sep 17 00:00:00 2001 From: Josh Pinkney Date: Fri, 14 Feb 2025 11:56:11 -0500 Subject: [PATCH 1/6] config(amazonq): Add override settings for workspace context lsp --- packages/amazonq/src/lsp/activation.ts | 4 +- packages/amazonq/src/lsp/config.ts | 14 +- packages/amazonq/src/lsp/lspInstaller.ts | 66 +++----- .../amazonq/test/e2e/lsp/lspInstaller.test.ts | 12 +- .../test/unit/amazonq/lsp/config.test.ts | 141 +++++++++++------- packages/core/src/amazonq/index.ts | 1 + packages/core/src/amazonq/lsp/config.ts | 29 ++++ .../core/src/amazonq/lsp/lspController.ts | 4 +- .../src/amazonq/lsp/workspaceInstaller.ts | 59 +++----- packages/core/src/dev/activation.ts | 4 +- packages/core/src/shared/index.ts | 1 + packages/core/src/shared/lsp/lspInstaller.ts | 57 +++++++ packages/core/src/shared/settings.ts | 2 + 13 files changed, 232 insertions(+), 162 deletions(-) create mode 100644 packages/core/src/amazonq/lsp/config.ts create mode 100644 packages/core/src/shared/lsp/lspInstaller.ts diff --git a/packages/amazonq/src/lsp/activation.ts b/packages/amazonq/src/lsp/activation.ts index 783c35ddc94..13bc0e67484 100644 --- a/packages/amazonq/src/lsp/activation.ts +++ b/packages/amazonq/src/lsp/activation.ts @@ -5,13 +5,13 @@ import vscode from 'vscode' import { startLanguageServer } from './client' -import { AmazonQLSPResolver } from './lspInstaller' +import { AmazonQLSPInstaller } from './lspInstaller' import { Commands, lspSetupStage, ToolkitError } from 'aws-core-vscode/shared' export async function activate(ctx: vscode.ExtensionContext): Promise { try { await lspSetupStage('all', async () => { - const installResult = await new AmazonQLSPResolver().resolve() + const installResult = await new AmazonQLSPInstaller().resolve() await lspSetupStage('launch', async () => await startLanguageServer(ctx, installResult.resourcePaths)) }) ctx.subscriptions.push( diff --git a/packages/amazonq/src/lsp/config.ts b/packages/amazonq/src/lsp/config.ts index 44b08fbc396..25b7d5bd1c9 100644 --- a/packages/amazonq/src/lsp/config.ts +++ b/packages/amazonq/src/lsp/config.ts @@ -4,25 +4,19 @@ */ import { DevSettings, getServiceEnvVarConfig } from 'aws-core-vscode/shared' +import { LspConfig } from 'aws-core-vscode/amazonq' -export interface AmazonQLspConfig { - manifestUrl: string - supportedVersions: string - id: string - locationOverride?: string -} - -export const defaultAmazonQLspConfig: AmazonQLspConfig = { +export const defaultAmazonQLspConfig: LspConfig = { manifestUrl: 'https://aws-toolkit-language-servers.amazonaws.com/codewhisperer/0/manifest.json', supportedVersions: '^3.1.1', id: 'AmazonQ', // used for identification in global storage/local disk location. Do not change. locationOverride: undefined, } -export function getAmazonQLspConfig(): AmazonQLspConfig { +export function getAmazonQLspConfig(): LspConfig { return { ...defaultAmazonQLspConfig, - ...(DevSettings.instance.getServiceConfig('amazonqLsp', {}) as AmazonQLspConfig), + ...(DevSettings.instance.getServiceConfig('amazonqLsp', {}) as LspConfig), ...getServiceEnvVarConfig('amazonqLsp', Object.keys(defaultAmazonQLspConfig)), } } diff --git a/packages/amazonq/src/lsp/lspInstaller.ts b/packages/amazonq/src/lsp/lspInstaller.ts index e3be27a9580..ad01a6539e0 100644 --- a/packages/amazonq/src/lsp/lspInstaller.ts +++ b/packages/amazonq/src/lsp/lspInstaller.ts @@ -3,64 +3,32 @@ * SPDX-License-Identifier: Apache-2.0 */ -import * as vscode from 'vscode' -import { - ManifestResolver, - LanguageServerResolver, - LspResolver, - fs, - LspResolution, - getNodeExecutableName, - cleanLspDownloads, - getLogger, -} from 'aws-core-vscode/shared' +import { fs, getNodeExecutableName, BaseLspInstaller, ResourcePaths } from 'aws-core-vscode/shared' import path from 'path' -import { Range } from 'semver' import { getAmazonQLspConfig } from './config' -const logger = getLogger('amazonqLsp') +export class AmazonQLSPInstaller extends BaseLspInstaller { + constructor() { + super(getAmazonQLspConfig()) + } + + protected override async postInstall(assetDirectory: string): Promise { + const resourcePaths = this.resourcePaths(assetDirectory) + await fs.chmod(resourcePaths.node, 0o755) + } -export class AmazonQLSPResolver implements LspResolver { - async resolve(): Promise { - const { id, manifestUrl, supportedVersions, locationOverride } = getAmazonQLspConfig() - if (locationOverride) { - void vscode.window.showInformationMessage(`Using language server override location: ${locationOverride}`) + protected override resourcePaths(assetDirectory?: string): ResourcePaths { + if (!assetDirectory) { return { - assetDirectory: locationOverride, - location: 'override', - version: '0.0.0', - resourcePaths: { - lsp: locationOverride, - node: getNodeExecutableName(), - }, + lsp: this.config.locationOverride ?? '', + node: getNodeExecutableName(), } } - // "AmazonQ" is shared across toolkits to provide a common access point, don't change it - const manifest = await new ManifestResolver(manifestUrl, id).resolve() - const installationResult = await new LanguageServerResolver( - manifest, - id, - new Range(supportedVersions, { - includePrerelease: true, - }) - ).resolve() - - const nodePath = path.join(installationResult.assetDirectory, `servers/${getNodeExecutableName()}`) - await fs.chmod(nodePath, 0o755) - - const deletedVersions = await cleanLspDownloads( - manifest.versions, - path.dirname(installationResult.assetDirectory) - ) - logger.debug(`Cleaned up ${deletedVersions.length} old versions`) - + const nodePath = path.join(assetDirectory, `servers/${getNodeExecutableName()}`) return { - ...installationResult, - resourcePaths: { - lsp: path.join(installationResult.assetDirectory, 'servers/aws-lsp-codewhisperer.js'), - node: nodePath, - }, + lsp: path.join(assetDirectory, 'servers/aws-lsp-codewhisperer.js'), + node: nodePath, } } } diff --git a/packages/amazonq/test/e2e/lsp/lspInstaller.test.ts b/packages/amazonq/test/e2e/lsp/lspInstaller.test.ts index 0447d7f5fcf..99792494312 100644 --- a/packages/amazonq/test/e2e/lsp/lspInstaller.test.ts +++ b/packages/amazonq/test/e2e/lsp/lspInstaller.test.ts @@ -5,7 +5,7 @@ import assert from 'assert' import sinon from 'sinon' -import { AmazonQLSPResolver } from '../../../src/lsp/lspInstaller' +import { AmazonQLSPInstaller } from '../../../src/lsp/lspInstaller' import { DevSettings, fs, @@ -18,9 +18,9 @@ import { } from 'aws-core-vscode/shared' import * as semver from 'semver' import { assertTelemetry } from 'aws-core-vscode/test' -import { LspController } from 'aws-core-vscode/amazonq' +import { LspConfig, LspController } from 'aws-core-vscode/amazonq' import { LanguageServerSetup } from 'aws-core-vscode/telemetry' -import { AmazonQLspConfig, getAmazonQLspConfig } from '../../../src/lsp/config' +import { getAmazonQLspConfig } from '../../../src/lsp/config' function createVersion(version: string) { return { @@ -44,13 +44,13 @@ function createVersion(version: string) { } describe('AmazonQLSPInstaller', () => { - let resolver: AmazonQLSPResolver + let resolver: AmazonQLSPInstaller let sandbox: sinon.SinonSandbox let tempDir: string // If globalState contains an ETag that is up to date with remote, we won't fetch it resulting in inconsistent behavior. // Therefore, we clear it temporarily for these tests to ensure consistent behavior. let manifestStorage: { [key: string]: any } - let lspConfig: AmazonQLspConfig + let lspConfig: LspConfig before(async () => { manifestStorage = globals.globalState.get(manifestStorageKey) || {} @@ -59,7 +59,7 @@ describe('AmazonQLSPInstaller', () => { beforeEach(async () => { sandbox = sinon.createSandbox() - resolver = new AmazonQLSPResolver() + resolver = new AmazonQLSPInstaller() tempDir = await makeTemporaryToolkitFolder() sandbox.stub(LanguageServerResolver.prototype, 'defaultDownloadFolder').returns(tempDir) // Called on extension activation and can contaminate telemetry. diff --git a/packages/amazonq/test/unit/amazonq/lsp/config.test.ts b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts index c26e4a87239..83ab40c4ea7 100644 --- a/packages/amazonq/test/unit/amazonq/lsp/config.test.ts +++ b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts @@ -7,73 +7,104 @@ import assert from 'assert' import { DevSettings } from 'aws-core-vscode/shared' import sinon from 'sinon' import { defaultAmazonQLspConfig, getAmazonQLspConfig } from '../../../../src/lsp/config' +import { LspConfig, getAmazonQWorkspaceLspConfig, defaultAmazonQWorkspaceLspConfig } from 'aws-core-vscode/amazonq' -describe('getAmazonQLspConfig', () => { - let sandbox: sinon.SinonSandbox - let serviceConfigStub: sinon.SinonStub - const settingConfig = { - manifestUrl: 'https://custom.url/manifest.json', - supportedVersions: '4.0.0', - id: 'AmazonQSetting', - locationOverride: '/custom/path', - } +for (const [name, config, defaultConfig, setEnv, resetEnv] of [ + [ + 'getAmazonQLspConfig', + getAmazonQLspConfig, + defaultAmazonQLspConfig, + (envConfig: LspConfig) => { + process.env.__AMAZONQLSP_MANIFEST_URL = envConfig.manifestUrl + process.env.__AMAZONQLSP_SUPPORTED_VERSIONS = envConfig.supportedVersions + process.env.__AMAZONQLSP_ID = envConfig.id + process.env.__AMAZONQLSP_LOCATION_OVERRIDE = envConfig.locationOverride + }, + () => { + delete process.env.__AMAZONQLSP_MANIFEST_URL + delete process.env.__AMAZONQLSP_SUPPORTED_VERSIONS + delete process.env.__AMAZONQLSP_ID + delete process.env.__AMAZONQLSP_LOCATION_OVERRIDE + }, + ], + [ + 'getAmazonQWorkspaceLspConfig', + getAmazonQWorkspaceLspConfig, + defaultAmazonQWorkspaceLspConfig, + (envConfig: LspConfig) => { + process.env.__AMAZONQWORKSPACELSP_MANIFEST_URL = envConfig.manifestUrl + process.env.__AMAZONQWORKSPACELSP_SUPPORTED_VERSIONS = envConfig.supportedVersions + process.env.__AMAZONQWORKSPACELSP_ID = envConfig.id + process.env.__AMAZONQWORKSPACELSP_LOCATION_OVERRIDE = envConfig.locationOverride + }, + () => { + delete process.env.__AMAZONQWORKSPACELSP_MANIFEST_URL + delete process.env.__AMAZONQWORKSPACELSP_SUPPORTED_VERSIONS + delete process.env.__AMAZONQWORKSPACELSP_ID + delete process.env.__AMAZONQWORKSPACELSP_LOCATION_OVERRIDE + }, + ], +] as const) { + describe(name, () => { + let sandbox: sinon.SinonSandbox + let serviceConfigStub: sinon.SinonStub + const settingConfig: LspConfig = { + manifestUrl: 'https://custom.url/manifest.json', + supportedVersions: '4.0.0', + id: 'AmazonQSetting', + locationOverride: '/custom/path', + } - beforeEach(() => { - sandbox = sinon.createSandbox() + beforeEach(() => { + sandbox = sinon.createSandbox() - serviceConfigStub = sandbox.stub() - sandbox.stub(DevSettings, 'instance').get(() => ({ - getServiceConfig: serviceConfigStub, - })) - }) + serviceConfigStub = sandbox.stub() + sandbox.stub(DevSettings, 'instance').get(() => ({ + getServiceConfig: serviceConfigStub, + })) + }) - afterEach(() => { - sandbox.restore() - }) + afterEach(() => { + sandbox.restore() + resetEnv() + }) - it('uses default config', () => { - serviceConfigStub.returns({}) - const config = getAmazonQLspConfig() - assert.deepStrictEqual(config, defaultAmazonQLspConfig) - }) + it('uses default config', () => { + serviceConfigStub.returns({}) + assert.deepStrictEqual(config(), defaultConfig) + }) - it('overrides location', () => { - const locationOverride = '/custom/path/to/lsp' - serviceConfigStub.returns({ locationOverride }) + it('overrides location', () => { + const locationOverride = '/custom/path/to/lsp' + serviceConfigStub.returns({ locationOverride }) - const config = getAmazonQLspConfig() - assert.deepStrictEqual(config, { - ...defaultAmazonQLspConfig, - locationOverride, + assert.deepStrictEqual(config(), { + ...defaultConfig, + locationOverride, + }) }) - }) - - it('overrides default settings', () => { - serviceConfigStub.returns(settingConfig) - const config = getAmazonQLspConfig() - assert.deepStrictEqual(config, settingConfig) - }) + it('overrides default settings', () => { + serviceConfigStub.returns(settingConfig) - it('environment variable takes precedence over settings', () => { - const envConfig = { - manifestUrl: 'https://another-custom.url/manifest.json', - supportedVersions: '5.1.1', - id: 'AmazonQEnv', - locationOverride: '/some/new/custom/path', - } + assert.deepStrictEqual(config(), settingConfig) + }) - process.env.__AMAZONQLSP_MANIFEST_URL = envConfig.manifestUrl - process.env.__AMAZONQLSP_SUPPORTED_VERSIONS = envConfig.supportedVersions - process.env.__AMAZONQLSP_ID = envConfig.id - process.env.__AMAZONQLSP_LOCATION_OVERRIDE = envConfig.locationOverride + it('environment variable takes precedence over settings', () => { + const envConfig: LspConfig = { + manifestUrl: 'https://another-custom.url/manifest.json', + supportedVersions: '5.1.1', + id: 'AmazonQEnv', + locationOverride: '/some/new/custom/path', + } - serviceConfigStub.returns(settingConfig) + setEnv(envConfig) + serviceConfigStub.returns(settingConfig) - const config = getAmazonQLspConfig() - assert.deepStrictEqual(config, { - ...defaultAmazonQLspConfig, - ...envConfig, + assert.deepStrictEqual(config(), { + ...defaultAmazonQLspConfig, + ...envConfig, + }) }) }) -}) +} diff --git a/packages/core/src/amazonq/index.ts b/packages/core/src/amazonq/index.ts index 84a3bf4f454..f1a285c7161 100644 --- a/packages/core/src/amazonq/index.ts +++ b/packages/core/src/amazonq/index.ts @@ -43,6 +43,7 @@ export { ExtensionMessage } from '../amazonq/webview/ui/commands' export { CodeReference } from '../codewhispererChat/view/connector/connector' export { extractAuthFollowUp } from './util/authUtils' export { Messenger } from './commons/connector/baseMessenger' +export * from './lsp/config' import { FeatureContext } from '../shared/featureConfig' /** diff --git a/packages/core/src/amazonq/lsp/config.ts b/packages/core/src/amazonq/lsp/config.ts new file mode 100644 index 00000000000..61d021dfc30 --- /dev/null +++ b/packages/core/src/amazonq/lsp/config.ts @@ -0,0 +1,29 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DevSettings } from '../../shared/settings' +import { getServiceEnvVarConfig } from '../../shared/vscode/env' + +export interface LspConfig { + manifestUrl: string + supportedVersions: string + id: string + locationOverride?: string +} + +export const defaultAmazonQWorkspaceLspConfig: LspConfig = { + manifestUrl: 'https://aws-toolkit-language-servers.amazonaws.com/q-context/manifest.json', + supportedVersions: '0.1.35', + id: 'AmazonQ-Workspace', // used for identification in global storage/local disk location. Do not change. + locationOverride: undefined, +} + +export function getAmazonQWorkspaceLspConfig(): LspConfig { + return { + ...defaultAmazonQWorkspaceLspConfig, + ...(DevSettings.instance.getServiceConfig('amazonqWorkspaceLsp', {}) as LspConfig), + ...getServiceEnvVarConfig('amazonqWorkspaceLsp', Object.keys(defaultAmazonQWorkspaceLspConfig)), + } +} diff --git a/packages/core/src/amazonq/lsp/lspController.ts b/packages/core/src/amazonq/lsp/lspController.ts index 2ed3065cc29..62ac37cd1d8 100644 --- a/packages/core/src/amazonq/lsp/lspController.ts +++ b/packages/core/src/amazonq/lsp/lspController.ts @@ -14,7 +14,7 @@ import { telemetry } from '../../shared/telemetry/telemetry' import { isCloud9 } from '../../shared/extensionUtilities' import globals, { isWeb } from '../../shared/extensionGlobals' import { isAmazonInternalOs } from '../../shared/vscode/env' -import { WorkspaceLSPResolver } from './workspaceInstaller' +import { WorkspaceLSPInstaller } from './workspaceInstaller' import { lspSetupStage } from '../../shared/lsp/utils/setupStage' export interface Chunk { @@ -186,7 +186,7 @@ export class LspController { private async setupLsp(context: vscode.ExtensionContext) { await lspSetupStage('all', async () => { - const installResult = await new WorkspaceLSPResolver().resolve() + const installResult = await new WorkspaceLSPInstaller().resolve() await lspSetupStage('launch', async () => activateLsp(context, installResult.resourcePaths)) this.logger.info('LspController: LSP activated') }) diff --git a/packages/core/src/amazonq/lsp/workspaceInstaller.ts b/packages/core/src/amazonq/lsp/workspaceInstaller.ts index 849fd3a37c5..83f3a4c283a 100644 --- a/packages/core/src/amazonq/lsp/workspaceInstaller.ts +++ b/packages/core/src/amazonq/lsp/workspaceInstaller.ts @@ -4,49 +4,36 @@ */ import path from 'path' -import { LspResolution, LspResolver } from '../../shared/lsp/types' -import { ManifestResolver } from '../../shared/lsp/manifestResolver' -import { LanguageServerResolver } from '../../shared/lsp/lspResolver' -import { Range } from 'semver' +import { ResourcePaths } from '../../shared/lsp/types' import { getNodeExecutableName } from '../../shared/lsp/utils/platform' import { fs } from '../../shared/fs/fs' -import { cleanLspDownloads } from '../../shared/lsp/utils/cleanup' -import { getLogger } from '../../shared/logger/logger' +import { BaseLspInstaller } from '../../shared/lsp/lspInstaller' +import { getAmazonQWorkspaceLspConfig } from './config' -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.35' -const logger = getLogger('amazonqWorkspaceLsp') +export class WorkspaceLSPInstaller extends BaseLspInstaller { + constructor() { + super(getAmazonQWorkspaceLspConfig()) + } -export class WorkspaceLSPResolver implements LspResolver { - async resolve(): Promise { - const name = 'AmazonQ-Workspace' - const manifest = await new ManifestResolver(manifestUrl, name).resolve() - const installationResult = await new LanguageServerResolver( - manifest, - name, - new Range(supportedLspServerVersions) - ).resolve() + protected override async postInstall(assetDirectory: string): Promise { + const resourcePaths = this.resourcePaths(assetDirectory) + await fs.chmod(resourcePaths.node, 0o755) + } - const nodeName = - process.platform === 'win32' ? getNodeExecutableName() : `node-${process.platform}-${process.arch}` - const nodePath = path.join(installationResult.assetDirectory, nodeName) - await fs.chmod(nodePath, 0o755) + protected override resourcePaths(assetDirectory?: string): ResourcePaths { + // local version + if (!assetDirectory) { + return { + lsp: this.config.locationOverride ?? '', + node: getNodeExecutableName(), + } + } - const deletedVersions = await cleanLspDownloads( - manifest.versions, - path.basename(installationResult.assetDirectory) - ) - logger.debug(`cleaning old LSP versions deleted ${deletedVersions.length} versions`) + const lspNodeName = + process.platform === 'win32' ? getNodeExecutableName() : `node-${process.platform}-${process.arch}` return { - ...installationResult, - resourcePaths: { - lsp: path.join( - installationResult.assetDirectory, - `qserver-${process.platform}-${process.arch}/qserver/lspServer.js` - ), - node: nodePath, - }, + lsp: path.join(assetDirectory, `qserver-${process.platform}-${process.arch}/qserver/lspServer.js`), + node: path.join(assetDirectory, lspNodeName), } } } diff --git a/packages/core/src/dev/activation.ts b/packages/core/src/dev/activation.ts index a712e984dfb..7f0c6290435 100644 --- a/packages/core/src/dev/activation.ts +++ b/packages/core/src/dev/activation.ts @@ -25,7 +25,7 @@ import { NotificationsController } from '../notifications/controller' import { DevNotificationsState } from '../notifications/types' import { QuickPickItem } from 'vscode' import { ChildProcess } from '../shared/utilities/processUtils' -import { WorkspaceLSPResolver } from '../amazonq/lsp/workspaceInstaller' +import { WorkspaceLSPInstaller } from '../amazonq/lsp/workspaceInstaller' interface MenuOption { readonly label: string @@ -546,7 +546,7 @@ async function resetNotificationsState() { } async function resetWorkspaceLspDownload() { - await new WorkspaceLSPResolver().resolve() + await new WorkspaceLSPInstaller().resolve() } async function editNotifications() { diff --git a/packages/core/src/shared/index.ts b/packages/core/src/shared/index.ts index 6193a68445c..0b82bfd4123 100644 --- a/packages/core/src/shared/index.ts +++ b/packages/core/src/shared/index.ts @@ -70,3 +70,4 @@ export * from './lsp/utils/cleanup' export { default as request } from './request' export * from './lsp/utils/platform' export * as processUtils from './utilities/processUtils' +export * from './lsp/lspInstaller' diff --git a/packages/core/src/shared/lsp/lspInstaller.ts b/packages/core/src/shared/lsp/lspInstaller.ts new file mode 100644 index 00000000000..4ef8f68075f --- /dev/null +++ b/packages/core/src/shared/lsp/lspInstaller.ts @@ -0,0 +1,57 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import path from 'path' +import vscode from 'vscode' +import { LspConfig } from '../../amazonq/lsp/config' +import { LanguageServerResolver } from './lspResolver' +import { ManifestResolver } from './manifestResolver' +import { LspResolution, ResourcePaths } from './types' +import { cleanLspDownloads } from './utils/cleanup' +import { Range } from 'semver' +import { getLogger } from '../logger/logger' + +const logger = getLogger('lsp') + +export abstract class BaseLspInstaller { + constructor(protected config: LspConfig) {} + + async resolve(): Promise { + const { id, manifestUrl, supportedVersions, locationOverride } = this.config + if (locationOverride) { + void vscode.window.showInformationMessage(`Using language server override location: ${locationOverride}`) + return { + assetDirectory: locationOverride, + location: 'override', + version: '0.0.0', + resourcePaths: this.resourcePaths(), + } + } + + const manifest = await new ManifestResolver(manifestUrl, id).resolve() + const installationResult = await new LanguageServerResolver( + manifest, + id, + new Range(supportedVersions, { + includePrerelease: true, + }) + ).resolve() + + const assetDirectory = installationResult.assetDirectory + + await this.postInstall(assetDirectory) + + const deletedVersions = await cleanLspDownloads(manifest.versions, path.dirname(assetDirectory)) + logger.debug(`cleaning old LSP versions deleted ${deletedVersions.length} versions`) + + return { + ...installationResult, + resourcePaths: this.resourcePaths(assetDirectory), + } + } + + protected abstract postInstall(assetDirectory: string): Promise + protected abstract resourcePaths(assetDirectory?: string): ResourcePaths +} diff --git a/packages/core/src/shared/settings.ts b/packages/core/src/shared/settings.ts index 09b7396f0d1..180244e6f6f 100644 --- a/packages/core/src/shared/settings.ts +++ b/packages/core/src/shared/settings.ts @@ -758,6 +758,7 @@ const devSettings = { codecatalystService: Record(String, String), codewhispererService: Record(String, String), amazonqLsp: Record(String, String), + amazonqWorkspaceLsp: Record(String, String), ssoCacheDirectory: String, autofillStartUrl: String, webAuth: Boolean, @@ -771,6 +772,7 @@ interface ServiceTypeMap { codecatalystService: codecatalyst.CodeCatalystConfig codewhispererService: codewhisperer.CodeWhispererConfig amazonqLsp: object // type is provided inside of amazon q + amazonqWorkspaceLsp: object // type is provided inside of amazon q } /** From b8da844eb05bf2b4f77bae7abc946b57f5cd821a Mon Sep 17 00:00:00 2001 From: Josh Pinkney Date: Fri, 14 Feb 2025 12:11:51 -0500 Subject: [PATCH 2/6] a --- CONTRIBUTING.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 302ee885009..21a9c8f62cd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -488,7 +488,11 @@ Unlike the user setting overrides, not all of these environment variables have t - `__AMAZONQLSP_MANIFEST_URL`: for aws.dev.amazonqLsp.manifestUrl - `__AMAZONQLSP_SUPPORTED_VERSIONS`: for aws.dev.amazonqLsp.supportedVersions - `__AMAZONQLSP_ID`: for aws.dev.amazonqLsp.id -- `__AMAZONQLSP_LOCATION_OVERRIDE`: for aws.dev.amazonqLsp.locationOverride +- `__AMAZONQLSP_LOCATION_OVERRIDE`: for aws.dev.amazonqWorkspaceLsp.locationOverride +- `__AMAZONQWORKSPACELSP_MANIFEST_URL`: for aws.dev.amazonqWorkspaceLsp.manifestUrl +- `__AMAZONQWORKSPACELSP_SUPPORTED_VERSIONS`: for aws.dev.amazonqWorkspaceLsp.supportedVersions +- `__AMAZONQWORKSPACELSP_ID`: for aws.dev.amazonqWorkspaceLsp.id +- `__AMAZONQWORKSPACELSP_LOCATION_OVERRIDE`: for aws.dev.amazonqWorkspaceLsp.locationOverride #### Lambda From d487484d65d65476568513a500294dc629e14b0e Mon Sep 17 00:00:00 2001 From: Josh Pinkney Date: Fri, 14 Feb 2025 12:39:30 -0500 Subject: [PATCH 3/6] save --- CONTRIBUTING.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 21a9c8f62cd..7a5e5217ea4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -440,6 +440,19 @@ Example: } ``` +Overrides specifically for the Amazon Q Workspace Context language server can be set using the `aws.dev.amazonqWorkspaceLsp` setting. This is a JSON object consisting of keys/values required to override language server: `manifestUrl`, `supportedVersions`, `id`, and `locationOverride`. + +Example: + +```json +"aws.dev.amazonqWorkspaceLsp": { + "manifestUrl": "https://custom.url/manifest.json", + "supportedVersions": "4.0.0", + "id": "AmazonQ", + "locationOverride": "/custom/path/to/local/lsp/folder", +} +``` + ### Environment variables Environment variables can be used to modify the behaviour of VSCode. The following are environment variables that can be used to configure the extension: From 5e4908a85c391c0be2af09a5c89f1f4e6dc2f709 Mon Sep 17 00:00:00 2001 From: Josh Pinkney Date: Wed, 19 Feb 2025 07:59:04 -0500 Subject: [PATCH 4/6] save --- CONTRIBUTING.md | 12 ++++---- packages/amazonq/src/lsp/activation.ts | 4 +-- packages/amazonq/src/lsp/lspInstaller.ts | 2 +- .../amazonq/test/e2e/lsp/lspInstaller.test.ts | 6 ++-- packages/core/src/amazonq/lsp/config.ts | 4 +-- .../src/amazonq/lsp/workspaceInstaller.ts | 2 +- packages/core/src/shared/index.ts | 2 +- .../{lspInstaller.ts => baseLspInstaller.ts} | 28 ++++++++++++------- 8 files changed, 34 insertions(+), 26 deletions(-) rename packages/core/src/shared/lsp/{lspInstaller.ts => baseLspInstaller.ts} (66%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7a5e5217ea4..0e23a20d71d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -427,7 +427,7 @@ Example: } ``` -Overrides specifically for the Amazon Q language server can be set using the `aws.dev.amazonqLsp` setting. This is a JSON object consisting of keys/values required to override language server: `manifestUrl`, `supportedVersions`, `id`, and `locationOverride`. +Overrides specifically for the Amazon Q language server can be set using the `aws.dev.amazonqLsp` setting. This is a JSON object consisting of keys/values required to override language server: `manifestUrl`, `supportedVersions`, `id`, and `path`. Example: @@ -436,11 +436,11 @@ Example: "manifestUrl": "https://custom.url/manifest.json", "supportedVersions": "4.0.0", "id": "AmazonQ", - "locationOverride": "/custom/path/to/local/lsp/folder", + "path": "/custom/path/to/local/lsp/folder", } ``` -Overrides specifically for the Amazon Q Workspace Context language server can be set using the `aws.dev.amazonqWorkspaceLsp` setting. This is a JSON object consisting of keys/values required to override language server: `manifestUrl`, `supportedVersions`, `id`, and `locationOverride`. +Overrides specifically for the Amazon Q Workspace Context language server can be set using the `aws.dev.amazonqWorkspaceLsp` setting. This is a JSON object consisting of keys/values required to override language server: `manifestUrl`, `supportedVersions`, `id`, and `path`. Example: @@ -449,7 +449,7 @@ Example: "manifestUrl": "https://custom.url/manifest.json", "supportedVersions": "4.0.0", "id": "AmazonQ", - "locationOverride": "/custom/path/to/local/lsp/folder", + "path": "/custom/path/to/local/lsp/folder", } ``` @@ -501,11 +501,11 @@ Unlike the user setting overrides, not all of these environment variables have t - `__AMAZONQLSP_MANIFEST_URL`: for aws.dev.amazonqLsp.manifestUrl - `__AMAZONQLSP_SUPPORTED_VERSIONS`: for aws.dev.amazonqLsp.supportedVersions - `__AMAZONQLSP_ID`: for aws.dev.amazonqLsp.id -- `__AMAZONQLSP_LOCATION_OVERRIDE`: for aws.dev.amazonqWorkspaceLsp.locationOverride +- `__AMAZONQLSP_PATH`: for aws.dev.amazonqWorkspaceLsp.locationOverride - `__AMAZONQWORKSPACELSP_MANIFEST_URL`: for aws.dev.amazonqWorkspaceLsp.manifestUrl - `__AMAZONQWORKSPACELSP_SUPPORTED_VERSIONS`: for aws.dev.amazonqWorkspaceLsp.supportedVersions - `__AMAZONQWORKSPACELSP_ID`: for aws.dev.amazonqWorkspaceLsp.id -- `__AMAZONQWORKSPACELSP_LOCATION_OVERRIDE`: for aws.dev.amazonqWorkspaceLsp.locationOverride +- `__AMAZONQWORKSPACELSP_PATH`: for aws.dev.amazonqWorkspaceLsp.locationOverride #### Lambda diff --git a/packages/amazonq/src/lsp/activation.ts b/packages/amazonq/src/lsp/activation.ts index 13bc0e67484..4d7918d76b0 100644 --- a/packages/amazonq/src/lsp/activation.ts +++ b/packages/amazonq/src/lsp/activation.ts @@ -5,13 +5,13 @@ import vscode from 'vscode' import { startLanguageServer } from './client' -import { AmazonQLSPInstaller } from './lspInstaller' +import { AmazonQLspInstaller } from './lspInstaller' import { Commands, lspSetupStage, ToolkitError } from 'aws-core-vscode/shared' export async function activate(ctx: vscode.ExtensionContext): Promise { try { await lspSetupStage('all', async () => { - const installResult = await new AmazonQLSPInstaller().resolve() + const installResult = await new AmazonQLspInstaller().resolve() await lspSetupStage('launch', async () => await startLanguageServer(ctx, installResult.resourcePaths)) }) ctx.subscriptions.push( diff --git a/packages/amazonq/src/lsp/lspInstaller.ts b/packages/amazonq/src/lsp/lspInstaller.ts index ad01a6539e0..b6a6a54c9df 100644 --- a/packages/amazonq/src/lsp/lspInstaller.ts +++ b/packages/amazonq/src/lsp/lspInstaller.ts @@ -7,7 +7,7 @@ import { fs, getNodeExecutableName, BaseLspInstaller, ResourcePaths } from 'aws- import path from 'path' import { getAmazonQLspConfig } from './config' -export class AmazonQLSPInstaller extends BaseLspInstaller { +export class AmazonQLspInstaller extends BaseLspInstaller { constructor() { super(getAmazonQLspConfig()) } diff --git a/packages/amazonq/test/e2e/lsp/lspInstaller.test.ts b/packages/amazonq/test/e2e/lsp/lspInstaller.test.ts index 99792494312..04752640050 100644 --- a/packages/amazonq/test/e2e/lsp/lspInstaller.test.ts +++ b/packages/amazonq/test/e2e/lsp/lspInstaller.test.ts @@ -5,7 +5,7 @@ import assert from 'assert' import sinon from 'sinon' -import { AmazonQLSPInstaller } from '../../../src/lsp/lspInstaller' +import { AmazonQLspInstaller } from '../../../src/lsp/lspInstaller' import { DevSettings, fs, @@ -44,7 +44,7 @@ function createVersion(version: string) { } describe('AmazonQLSPInstaller', () => { - let resolver: AmazonQLSPInstaller + let resolver: AmazonQLspInstaller let sandbox: sinon.SinonSandbox let tempDir: string // If globalState contains an ETag that is up to date with remote, we won't fetch it resulting in inconsistent behavior. @@ -59,7 +59,7 @@ describe('AmazonQLSPInstaller', () => { beforeEach(async () => { sandbox = sinon.createSandbox() - resolver = new AmazonQLSPInstaller() + resolver = new AmazonQLspInstaller() tempDir = await makeTemporaryToolkitFolder() sandbox.stub(LanguageServerResolver.prototype, 'defaultDownloadFolder').returns(tempDir) // Called on extension activation and can contaminate telemetry. diff --git a/packages/core/src/amazonq/lsp/config.ts b/packages/core/src/amazonq/lsp/config.ts index 61d021dfc30..1a597032a35 100644 --- a/packages/core/src/amazonq/lsp/config.ts +++ b/packages/core/src/amazonq/lsp/config.ts @@ -10,14 +10,14 @@ export interface LspConfig { manifestUrl: string supportedVersions: string id: string - locationOverride?: string + path?: string } export const defaultAmazonQWorkspaceLspConfig: LspConfig = { manifestUrl: 'https://aws-toolkit-language-servers.amazonaws.com/q-context/manifest.json', supportedVersions: '0.1.35', id: 'AmazonQ-Workspace', // used for identification in global storage/local disk location. Do not change. - locationOverride: undefined, + path: undefined, } export function getAmazonQWorkspaceLspConfig(): LspConfig { diff --git a/packages/core/src/amazonq/lsp/workspaceInstaller.ts b/packages/core/src/amazonq/lsp/workspaceInstaller.ts index 83f3a4c283a..3bc32e917a6 100644 --- a/packages/core/src/amazonq/lsp/workspaceInstaller.ts +++ b/packages/core/src/amazonq/lsp/workspaceInstaller.ts @@ -7,7 +7,7 @@ import path from 'path' import { ResourcePaths } from '../../shared/lsp/types' import { getNodeExecutableName } from '../../shared/lsp/utils/platform' import { fs } from '../../shared/fs/fs' -import { BaseLspInstaller } from '../../shared/lsp/lspInstaller' +import { BaseLspInstaller } from '../../shared/lsp/baseLspInstaller' import { getAmazonQWorkspaceLspConfig } from './config' export class WorkspaceLSPInstaller extends BaseLspInstaller { diff --git a/packages/core/src/shared/index.ts b/packages/core/src/shared/index.ts index 0b82bfd4123..f35f7c2daab 100644 --- a/packages/core/src/shared/index.ts +++ b/packages/core/src/shared/index.ts @@ -70,4 +70,4 @@ export * from './lsp/utils/cleanup' export { default as request } from './request' export * from './lsp/utils/platform' export * as processUtils from './utilities/processUtils' -export * from './lsp/lspInstaller' +export * as BaseLspInstaller from './lsp/baseLspInstaller' diff --git a/packages/core/src/shared/lsp/lspInstaller.ts b/packages/core/src/shared/lsp/baseLspInstaller.ts similarity index 66% rename from packages/core/src/shared/lsp/lspInstaller.ts rename to packages/core/src/shared/lsp/baseLspInstaller.ts index 4ef8f68075f..86014508328 100644 --- a/packages/core/src/shared/lsp/lspInstaller.ts +++ b/packages/core/src/shared/lsp/baseLspInstaller.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import path from 'path' +import * as nodePath from 'path' import vscode from 'vscode' import { LspConfig } from '../../amazonq/lsp/config' import { LanguageServerResolver } from './lspResolver' @@ -12,18 +12,26 @@ import { LspResolution, ResourcePaths } from './types' import { cleanLspDownloads } from './utils/cleanup' import { Range } from 'semver' import { getLogger } from '../logger/logger' - -const logger = getLogger('lsp') +import type { Logger, LogTopic } from '../logger/logger' export abstract class BaseLspInstaller { - constructor(protected config: LspConfig) {} + private logger: Logger + + constructor( + protected config: LspConfig, + loggerName: Extract + ) { + this.logger = getLogger(loggerName) + } async resolve(): Promise { - const { id, manifestUrl, supportedVersions, locationOverride } = this.config - if (locationOverride) { - void vscode.window.showInformationMessage(`Using language server override location: ${locationOverride}`) + const { id, manifestUrl, supportedVersions, path } = this.config + if (path) { + const overrideMsg = `Using language server override location: ${path}` + this.logger.info(overrideMsg) + void vscode.window.showInformationMessage(overrideMsg) return { - assetDirectory: locationOverride, + assetDirectory: path, location: 'override', version: '0.0.0', resourcePaths: this.resourcePaths(), @@ -43,8 +51,8 @@ export abstract class BaseLspInstaller { await this.postInstall(assetDirectory) - const deletedVersions = await cleanLspDownloads(manifest.versions, path.dirname(assetDirectory)) - logger.debug(`cleaning old LSP versions deleted ${deletedVersions.length} versions`) + const deletedVersions = await cleanLspDownloads(manifest.versions, nodePath.dirname(assetDirectory)) + this.logger.debug(`cleaning old LSP versions deleted ${deletedVersions.length} versions`) return { ...installationResult, From 520dbf0136215283c7cb5cb50b76f6fa1e0b5fd3 Mon Sep 17 00:00:00 2001 From: Josh Pinkney Date: Wed, 19 Feb 2025 08:05:16 -0500 Subject: [PATCH 5/6] fixup --- packages/amazonq/src/lsp/lspInstaller.ts | 4 ++-- packages/core/src/amazonq/lsp/workspaceInstaller.ts | 4 ++-- packages/core/src/shared/lsp/baseLspInstaller.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/amazonq/src/lsp/lspInstaller.ts b/packages/amazonq/src/lsp/lspInstaller.ts index b6a6a54c9df..d7c2a8f2761 100644 --- a/packages/amazonq/src/lsp/lspInstaller.ts +++ b/packages/amazonq/src/lsp/lspInstaller.ts @@ -9,7 +9,7 @@ import { getAmazonQLspConfig } from './config' export class AmazonQLspInstaller extends BaseLspInstaller { constructor() { - super(getAmazonQLspConfig()) + super(getAmazonQLspConfig(), 'amazonqLsp') } protected override async postInstall(assetDirectory: string): Promise { @@ -20,7 +20,7 @@ export class AmazonQLspInstaller extends BaseLspInstaller { protected override resourcePaths(assetDirectory?: string): ResourcePaths { if (!assetDirectory) { return { - lsp: this.config.locationOverride ?? '', + lsp: this.config.path ?? '', node: getNodeExecutableName(), } } diff --git a/packages/core/src/amazonq/lsp/workspaceInstaller.ts b/packages/core/src/amazonq/lsp/workspaceInstaller.ts index 3bc32e917a6..604aa89a9fd 100644 --- a/packages/core/src/amazonq/lsp/workspaceInstaller.ts +++ b/packages/core/src/amazonq/lsp/workspaceInstaller.ts @@ -12,7 +12,7 @@ import { getAmazonQWorkspaceLspConfig } from './config' export class WorkspaceLSPInstaller extends BaseLspInstaller { constructor() { - super(getAmazonQWorkspaceLspConfig()) + super(getAmazonQWorkspaceLspConfig(), 'amazonqWorkspaceLsp') } protected override async postInstall(assetDirectory: string): Promise { @@ -24,7 +24,7 @@ export class WorkspaceLSPInstaller extends BaseLspInstaller { // local version if (!assetDirectory) { return { - lsp: this.config.locationOverride ?? '', + lsp: this.config.path ?? '', node: getNodeExecutableName(), } } diff --git a/packages/core/src/shared/lsp/baseLspInstaller.ts b/packages/core/src/shared/lsp/baseLspInstaller.ts index 86014508328..ccc229e9445 100644 --- a/packages/core/src/shared/lsp/baseLspInstaller.ts +++ b/packages/core/src/shared/lsp/baseLspInstaller.ts @@ -19,7 +19,7 @@ export abstract class BaseLspInstaller { constructor( protected config: LspConfig, - loggerName: Extract + loggerName: Extract ) { this.logger = getLogger(loggerName) } From bf4ece094aa137095b885101a77d68746d6e132b Mon Sep 17 00:00:00 2001 From: Josh Pinkney Date: Wed, 19 Feb 2025 08:48:18 -0500 Subject: [PATCH 6/6] fix build --- packages/amazonq/src/lsp/config.ts | 2 +- packages/amazonq/src/lsp/lspInstaller.ts | 2 +- .../test/unit/amazonq/lsp/config.test.ts | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/amazonq/src/lsp/config.ts b/packages/amazonq/src/lsp/config.ts index 25b7d5bd1c9..634cc43aab2 100644 --- a/packages/amazonq/src/lsp/config.ts +++ b/packages/amazonq/src/lsp/config.ts @@ -10,7 +10,7 @@ export const defaultAmazonQLspConfig: LspConfig = { manifestUrl: 'https://aws-toolkit-language-servers.amazonaws.com/codewhisperer/0/manifest.json', supportedVersions: '^3.1.1', id: 'AmazonQ', // used for identification in global storage/local disk location. Do not change. - locationOverride: undefined, + path: undefined, } export function getAmazonQLspConfig(): LspConfig { diff --git a/packages/amazonq/src/lsp/lspInstaller.ts b/packages/amazonq/src/lsp/lspInstaller.ts index d7c2a8f2761..e041682ee74 100644 --- a/packages/amazonq/src/lsp/lspInstaller.ts +++ b/packages/amazonq/src/lsp/lspInstaller.ts @@ -7,7 +7,7 @@ import { fs, getNodeExecutableName, BaseLspInstaller, ResourcePaths } from 'aws- import path from 'path' import { getAmazonQLspConfig } from './config' -export class AmazonQLspInstaller extends BaseLspInstaller { +export class AmazonQLspInstaller extends BaseLspInstaller.BaseLspInstaller { constructor() { super(getAmazonQLspConfig(), 'amazonqLsp') } diff --git a/packages/amazonq/test/unit/amazonq/lsp/config.test.ts b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts index 83ab40c4ea7..9a9ba1ef348 100644 --- a/packages/amazonq/test/unit/amazonq/lsp/config.test.ts +++ b/packages/amazonq/test/unit/amazonq/lsp/config.test.ts @@ -18,13 +18,13 @@ for (const [name, config, defaultConfig, setEnv, resetEnv] of [ process.env.__AMAZONQLSP_MANIFEST_URL = envConfig.manifestUrl process.env.__AMAZONQLSP_SUPPORTED_VERSIONS = envConfig.supportedVersions process.env.__AMAZONQLSP_ID = envConfig.id - process.env.__AMAZONQLSP_LOCATION_OVERRIDE = envConfig.locationOverride + process.env.__AMAZONQLSP_PATH = envConfig.path }, () => { delete process.env.__AMAZONQLSP_MANIFEST_URL delete process.env.__AMAZONQLSP_SUPPORTED_VERSIONS delete process.env.__AMAZONQLSP_ID - delete process.env.__AMAZONQLSP_LOCATION_OVERRIDE + delete process.env.__AMAZONQLSP_PATH }, ], [ @@ -35,13 +35,13 @@ for (const [name, config, defaultConfig, setEnv, resetEnv] of [ process.env.__AMAZONQWORKSPACELSP_MANIFEST_URL = envConfig.manifestUrl process.env.__AMAZONQWORKSPACELSP_SUPPORTED_VERSIONS = envConfig.supportedVersions process.env.__AMAZONQWORKSPACELSP_ID = envConfig.id - process.env.__AMAZONQWORKSPACELSP_LOCATION_OVERRIDE = envConfig.locationOverride + process.env.__AMAZONQWORKSPACELSP_PATH = envConfig.path }, () => { delete process.env.__AMAZONQWORKSPACELSP_MANIFEST_URL delete process.env.__AMAZONQWORKSPACELSP_SUPPORTED_VERSIONS delete process.env.__AMAZONQWORKSPACELSP_ID - delete process.env.__AMAZONQWORKSPACELSP_LOCATION_OVERRIDE + delete process.env.__AMAZONQWORKSPACELSP_PATH }, ], ] as const) { @@ -52,7 +52,7 @@ for (const [name, config, defaultConfig, setEnv, resetEnv] of [ manifestUrl: 'https://custom.url/manifest.json', supportedVersions: '4.0.0', id: 'AmazonQSetting', - locationOverride: '/custom/path', + path: '/custom/path', } beforeEach(() => { @@ -75,12 +75,12 @@ for (const [name, config, defaultConfig, setEnv, resetEnv] of [ }) it('overrides location', () => { - const locationOverride = '/custom/path/to/lsp' - serviceConfigStub.returns({ locationOverride }) + const path = '/custom/path/to/lsp' + serviceConfigStub.returns({ path }) assert.deepStrictEqual(config(), { ...defaultConfig, - locationOverride, + path, }) }) @@ -95,7 +95,7 @@ for (const [name, config, defaultConfig, setEnv, resetEnv] of [ manifestUrl: 'https://another-custom.url/manifest.json', supportedVersions: '5.1.1', id: 'AmazonQEnv', - locationOverride: '/some/new/custom/path', + path: '/some/new/custom/path', } setEnv(envConfig)