diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 302ee885009..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,7 +436,20 @@ 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 `path`. + +Example: + +```json +"aws.dev.amazonqWorkspaceLsp": { + "manifestUrl": "https://custom.url/manifest.json", + "supportedVersions": "4.0.0", + "id": "AmazonQ", + "path": "/custom/path/to/local/lsp/folder", } ``` @@ -488,7 +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.amazonqLsp.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_PATH`: for aws.dev.amazonqWorkspaceLsp.locationOverride #### Lambda diff --git a/packages/amazonq/src/lsp/activation.ts b/packages/amazonq/src/lsp/activation.ts index 783c35ddc94..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 { 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..634cc43aab2 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, + path: 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..e041682ee74 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.BaseLspInstaller { + constructor() { + super(getAmazonQLspConfig(), 'amazonqLsp') + } + + 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.path ?? '', + 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..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 { 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..9a9ba1ef348 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_PATH = envConfig.path + }, + () => { + delete process.env.__AMAZONQLSP_MANIFEST_URL + delete process.env.__AMAZONQLSP_SUPPORTED_VERSIONS + delete process.env.__AMAZONQLSP_ID + delete process.env.__AMAZONQLSP_PATH + }, + ], + [ + '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_PATH = envConfig.path + }, + () => { + delete process.env.__AMAZONQWORKSPACELSP_MANIFEST_URL + delete process.env.__AMAZONQWORKSPACELSP_SUPPORTED_VERSIONS + delete process.env.__AMAZONQWORKSPACELSP_ID + delete process.env.__AMAZONQWORKSPACELSP_PATH + }, + ], +] 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', + path: '/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 path = '/custom/path/to/lsp' + serviceConfigStub.returns({ path }) - const config = getAmazonQLspConfig() - assert.deepStrictEqual(config, { - ...defaultAmazonQLspConfig, - locationOverride, + assert.deepStrictEqual(config(), { + ...defaultConfig, + path, + }) }) - }) - - 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', + path: '/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..1a597032a35 --- /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 + 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. + path: 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..604aa89a9fd 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/baseLspInstaller' +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(), 'amazonqWorkspaceLsp') + } -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.path ?? '', + 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..f35f7c2daab 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 * as BaseLspInstaller from './lsp/baseLspInstaller' diff --git a/packages/core/src/shared/lsp/baseLspInstaller.ts b/packages/core/src/shared/lsp/baseLspInstaller.ts new file mode 100644 index 00000000000..ccc229e9445 --- /dev/null +++ b/packages/core/src/shared/lsp/baseLspInstaller.ts @@ -0,0 +1,65 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as nodePath 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' +import type { Logger, LogTopic } from '../logger/logger' + +export abstract class BaseLspInstaller { + private logger: Logger + + constructor( + protected config: LspConfig, + loggerName: Extract + ) { + this.logger = getLogger(loggerName) + } + + async resolve(): Promise { + 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: path, + 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, nodePath.dirname(assetDirectory)) + this.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 } /**