Skip to content

Commit b8bec08

Browse files
committed
config(amazonq): Add override settings for workspace context lsp
1 parent b1d3e03 commit b8bec08

File tree

13 files changed

+232
-162
lines changed

13 files changed

+232
-162
lines changed

packages/amazonq/src/lsp/activation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55

66
import vscode from 'vscode'
77
import { startLanguageServer } from './client'
8-
import { AmazonQLSPResolver } from './lspInstaller'
8+
import { AmazonQLSPInstaller } from './lspInstaller'
99
import { Commands, lspSetupStage, ToolkitError } from 'aws-core-vscode/shared'
1010

1111
export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
1212
try {
1313
await lspSetupStage('all', async () => {
14-
const installResult = await new AmazonQLSPResolver().resolve()
14+
const installResult = await new AmazonQLSPInstaller().resolve()
1515
await lspSetupStage('launch', async () => await startLanguageServer(ctx, installResult.resourcePaths))
1616
})
1717
ctx.subscriptions.push(

packages/amazonq/src/lsp/config.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,19 @@
44
*/
55

66
import { DevSettings, getServiceEnvVarConfig } from 'aws-core-vscode/shared'
7+
import { LspConfig } from 'aws-core-vscode/amazonq'
78

8-
export interface AmazonQLspConfig {
9-
manifestUrl: string
10-
supportedVersions: string
11-
id: string
12-
locationOverride?: string
13-
}
14-
15-
export const defaultAmazonQLspConfig: AmazonQLspConfig = {
9+
export const defaultAmazonQLspConfig: LspConfig = {
1610
manifestUrl: 'https://aws-toolkit-language-servers.amazonaws.com/codewhisperer/0/manifest.json',
1711
supportedVersions: '^3.1.1',
1812
id: 'AmazonQ', // used for identification in global storage/local disk location. Do not change.
1913
locationOverride: undefined,
2014
}
2115

22-
export function getAmazonQLspConfig(): AmazonQLspConfig {
16+
export function getAmazonQLspConfig(): LspConfig {
2317
return {
2418
...defaultAmazonQLspConfig,
25-
...(DevSettings.instance.getServiceConfig('amazonqLsp', {}) as AmazonQLspConfig),
19+
...(DevSettings.instance.getServiceConfig('amazonqLsp', {}) as LspConfig),
2620
...getServiceEnvVarConfig('amazonqLsp', Object.keys(defaultAmazonQLspConfig)),
2721
}
2822
}

packages/amazonq/src/lsp/lspInstaller.ts

Lines changed: 17 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,64 +3,32 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import * as vscode from 'vscode'
7-
import {
8-
ManifestResolver,
9-
LanguageServerResolver,
10-
LspResolver,
11-
fs,
12-
LspResolution,
13-
getNodeExecutableName,
14-
cleanLspDownloads,
15-
getLogger,
16-
} from 'aws-core-vscode/shared'
6+
import { fs, getNodeExecutableName, BaseLspInstaller, ResourcePaths } from 'aws-core-vscode/shared'
177
import path from 'path'
18-
import { Range } from 'semver'
198
import { getAmazonQLspConfig } from './config'
209

21-
const logger = getLogger('amazonqLsp')
10+
export class AmazonQLSPInstaller extends BaseLspInstaller {
11+
constructor() {
12+
super(getAmazonQLspConfig())
13+
}
14+
15+
protected override async postInstall(assetDirectory: string): Promise<void> {
16+
const resourcePaths = this.resourcePaths(assetDirectory)
17+
await fs.chmod(resourcePaths.node, 0o755)
18+
}
2219

23-
export class AmazonQLSPResolver implements LspResolver {
24-
async resolve(): Promise<LspResolution> {
25-
const { id, manifestUrl, supportedVersions, locationOverride } = getAmazonQLspConfig()
26-
if (locationOverride) {
27-
void vscode.window.showInformationMessage(`Using language server override location: ${locationOverride}`)
20+
protected override resourcePaths(assetDirectory?: string): ResourcePaths {
21+
if (!assetDirectory) {
2822
return {
29-
assetDirectory: locationOverride,
30-
location: 'override',
31-
version: '0.0.0',
32-
resourcePaths: {
33-
lsp: locationOverride,
34-
node: getNodeExecutableName(),
35-
},
23+
lsp: this.config.locationOverride ?? '',
24+
node: getNodeExecutableName(),
3625
}
3726
}
3827

39-
// "AmazonQ" is shared across toolkits to provide a common access point, don't change it
40-
const manifest = await new ManifestResolver(manifestUrl, id).resolve()
41-
const installationResult = await new LanguageServerResolver(
42-
manifest,
43-
id,
44-
new Range(supportedVersions, {
45-
includePrerelease: true,
46-
})
47-
).resolve()
48-
49-
const nodePath = path.join(installationResult.assetDirectory, `servers/${getNodeExecutableName()}`)
50-
await fs.chmod(nodePath, 0o755)
51-
52-
const deletedVersions = await cleanLspDownloads(
53-
manifest.versions,
54-
path.dirname(installationResult.assetDirectory)
55-
)
56-
logger.debug(`Cleaned up ${deletedVersions.length} old versions`)
57-
28+
const nodePath = path.join(assetDirectory, `servers/${getNodeExecutableName()}`)
5829
return {
59-
...installationResult,
60-
resourcePaths: {
61-
lsp: path.join(installationResult.assetDirectory, 'servers/aws-lsp-codewhisperer.js'),
62-
node: nodePath,
63-
},
30+
lsp: path.join(assetDirectory, 'servers/aws-lsp-codewhisperer.js'),
31+
node: nodePath,
6432
}
6533
}
6634
}

packages/amazonq/test/e2e/lsp/lspInstaller.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import assert from 'assert'
77
import sinon from 'sinon'
8-
import { AmazonQLSPResolver } from '../../../src/lsp/lspInstaller'
8+
import { AmazonQLSPInstaller } from '../../../src/lsp/lspInstaller'
99
import {
1010
DevSettings,
1111
fs,
@@ -18,9 +18,9 @@ import {
1818
} from 'aws-core-vscode/shared'
1919
import * as semver from 'semver'
2020
import { assertTelemetry } from 'aws-core-vscode/test'
21-
import { LspController } from 'aws-core-vscode/amazonq'
21+
import { LspConfig, LspController } from 'aws-core-vscode/amazonq'
2222
import { LanguageServerSetup } from 'aws-core-vscode/telemetry'
23-
import { AmazonQLspConfig, getAmazonQLspConfig } from '../../../src/lsp/config'
23+
import { getAmazonQLspConfig } from '../../../src/lsp/config'
2424

2525
function createVersion(version: string) {
2626
return {
@@ -44,13 +44,13 @@ function createVersion(version: string) {
4444
}
4545

4646
describe('AmazonQLSPInstaller', () => {
47-
let resolver: AmazonQLSPResolver
47+
let resolver: AmazonQLSPInstaller
4848
let sandbox: sinon.SinonSandbox
4949
let tempDir: string
5050
// If globalState contains an ETag that is up to date with remote, we won't fetch it resulting in inconsistent behavior.
5151
// Therefore, we clear it temporarily for these tests to ensure consistent behavior.
5252
let manifestStorage: { [key: string]: any }
53-
let lspConfig: AmazonQLspConfig
53+
let lspConfig: LspConfig
5454

5555
before(async () => {
5656
manifestStorage = globals.globalState.get(manifestStorageKey) || {}
@@ -59,7 +59,7 @@ describe('AmazonQLSPInstaller', () => {
5959

6060
beforeEach(async () => {
6161
sandbox = sinon.createSandbox()
62-
resolver = new AmazonQLSPResolver()
62+
resolver = new AmazonQLSPInstaller()
6363
tempDir = await makeTemporaryToolkitFolder()
6464
sandbox.stub(LanguageServerResolver.prototype, 'defaultDownloadFolder').returns(tempDir)
6565
// Called on extension activation and can contaminate telemetry.

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

Lines changed: 86 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,73 +7,104 @@ import assert from 'assert'
77
import { DevSettings } from 'aws-core-vscode/shared'
88
import sinon from 'sinon'
99
import { defaultAmazonQLspConfig, getAmazonQLspConfig } from '../../../../src/lsp/config'
10+
import { LspConfig, getAmazonQWorkspaceLspConfig, defaultAmazonQWorkspaceLspConfig } from 'aws-core-vscode/amazonq'
1011

11-
describe('getAmazonQLspConfig', () => {
12-
let sandbox: sinon.SinonSandbox
13-
let serviceConfigStub: sinon.SinonStub
14-
const settingConfig = {
15-
manifestUrl: 'https://custom.url/manifest.json',
16-
supportedVersions: '4.0.0',
17-
id: 'AmazonQSetting',
18-
locationOverride: '/custom/path',
19-
}
12+
for (const [name, config, defaultConfig, setEnv, resetEnv] of [
13+
[
14+
'getAmazonQLspConfig',
15+
getAmazonQLspConfig,
16+
defaultAmazonQLspConfig,
17+
(envConfig: LspConfig) => {
18+
process.env.__AMAZONQLSP_MANIFEST_URL = envConfig.manifestUrl
19+
process.env.__AMAZONQLSP_SUPPORTED_VERSIONS = envConfig.supportedVersions
20+
process.env.__AMAZONQLSP_ID = envConfig.id
21+
process.env.__AMAZONQLSP_LOCATION_OVERRIDE = envConfig.locationOverride
22+
},
23+
() => {
24+
delete process.env.__AMAZONQLSP_MANIFEST_URL
25+
delete process.env.__AMAZONQLSP_SUPPORTED_VERSIONS
26+
delete process.env.__AMAZONQLSP_ID
27+
delete process.env.__AMAZONQLSP_LOCATION_OVERRIDE
28+
},
29+
],
30+
[
31+
'getAmazonQWorkspaceLspConfig',
32+
getAmazonQWorkspaceLspConfig,
33+
defaultAmazonQWorkspaceLspConfig,
34+
(envConfig: LspConfig) => {
35+
process.env.__AMAZONQWORKSPACELSP_MANIFEST_URL = envConfig.manifestUrl
36+
process.env.__AMAZONQWORKSPACELSP_SUPPORTED_VERSIONS = envConfig.supportedVersions
37+
process.env.__AMAZONQWORKSPACELSP_ID = envConfig.id
38+
process.env.__AMAZONQWORKSPACELSP_LOCATION_OVERRIDE = envConfig.locationOverride
39+
},
40+
() => {
41+
delete process.env.__AMAZONQWORKSPACELSP_MANIFEST_URL
42+
delete process.env.__AMAZONQWORKSPACELSP_SUPPORTED_VERSIONS
43+
delete process.env.__AMAZONQWORKSPACELSP_ID
44+
delete process.env.__AMAZONQWORKSPACELSP_LOCATION_OVERRIDE
45+
},
46+
],
47+
] as const) {
48+
describe(name, () => {
49+
let sandbox: sinon.SinonSandbox
50+
let serviceConfigStub: sinon.SinonStub
51+
const settingConfig: LspConfig = {
52+
manifestUrl: 'https://custom.url/manifest.json',
53+
supportedVersions: '4.0.0',
54+
id: 'AmazonQSetting',
55+
locationOverride: '/custom/path',
56+
}
2057

21-
beforeEach(() => {
22-
sandbox = sinon.createSandbox()
58+
beforeEach(() => {
59+
sandbox = sinon.createSandbox()
2360

24-
serviceConfigStub = sandbox.stub()
25-
sandbox.stub(DevSettings, 'instance').get(() => ({
26-
getServiceConfig: serviceConfigStub,
27-
}))
28-
})
61+
serviceConfigStub = sandbox.stub()
62+
sandbox.stub(DevSettings, 'instance').get(() => ({
63+
getServiceConfig: serviceConfigStub,
64+
}))
65+
})
2966

30-
afterEach(() => {
31-
sandbox.restore()
32-
})
67+
afterEach(() => {
68+
sandbox.restore()
69+
resetEnv()
70+
})
3371

34-
it('uses default config', () => {
35-
serviceConfigStub.returns({})
36-
const config = getAmazonQLspConfig()
37-
assert.deepStrictEqual(config, defaultAmazonQLspConfig)
38-
})
72+
it('uses default config', () => {
73+
serviceConfigStub.returns({})
74+
assert.deepStrictEqual(config(), defaultConfig)
75+
})
3976

40-
it('overrides location', () => {
41-
const locationOverride = '/custom/path/to/lsp'
42-
serviceConfigStub.returns({ locationOverride })
77+
it('overrides location', () => {
78+
const locationOverride = '/custom/path/to/lsp'
79+
serviceConfigStub.returns({ locationOverride })
4380

44-
const config = getAmazonQLspConfig()
45-
assert.deepStrictEqual(config, {
46-
...defaultAmazonQLspConfig,
47-
locationOverride,
81+
assert.deepStrictEqual(config(), {
82+
...defaultConfig,
83+
locationOverride,
84+
})
4885
})
49-
})
50-
51-
it('overrides default settings', () => {
52-
serviceConfigStub.returns(settingConfig)
5386

54-
const config = getAmazonQLspConfig()
55-
assert.deepStrictEqual(config, settingConfig)
56-
})
87+
it('overrides default settings', () => {
88+
serviceConfigStub.returns(settingConfig)
5789

58-
it('environment variable takes precedence over settings', () => {
59-
const envConfig = {
60-
manifestUrl: 'https://another-custom.url/manifest.json',
61-
supportedVersions: '5.1.1',
62-
id: 'AmazonQEnv',
63-
locationOverride: '/some/new/custom/path',
64-
}
90+
assert.deepStrictEqual(config(), settingConfig)
91+
})
6592

66-
process.env.__AMAZONQLSP_MANIFEST_URL = envConfig.manifestUrl
67-
process.env.__AMAZONQLSP_SUPPORTED_VERSIONS = envConfig.supportedVersions
68-
process.env.__AMAZONQLSP_ID = envConfig.id
69-
process.env.__AMAZONQLSP_LOCATION_OVERRIDE = envConfig.locationOverride
93+
it('environment variable takes precedence over settings', () => {
94+
const envConfig: LspConfig = {
95+
manifestUrl: 'https://another-custom.url/manifest.json',
96+
supportedVersions: '5.1.1',
97+
id: 'AmazonQEnv',
98+
locationOverride: '/some/new/custom/path',
99+
}
70100

71-
serviceConfigStub.returns(settingConfig)
101+
setEnv(envConfig)
102+
serviceConfigStub.returns(settingConfig)
72103

73-
const config = getAmazonQLspConfig()
74-
assert.deepStrictEqual(config, {
75-
...defaultAmazonQLspConfig,
76-
...envConfig,
104+
assert.deepStrictEqual(config(), {
105+
...defaultAmazonQLspConfig,
106+
...envConfig,
107+
})
77108
})
78109
})
79-
})
110+
}

packages/core/src/amazonq/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export { ExtensionMessage } from '../amazonq/webview/ui/commands'
4343
export { CodeReference } from '../codewhispererChat/view/connector/connector'
4444
export { extractAuthFollowUp } from './util/authUtils'
4545
export { Messenger } from './commons/connector/baseMessenger'
46+
export * from './lsp/config'
4647
import { FeatureContext } from '../shared/featureConfig'
4748

4849
/**
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { DevSettings } from '../../shared/settings'
7+
import { getServiceEnvVarConfig } from '../../shared/vscode/env'
8+
9+
export interface LspConfig {
10+
manifestUrl: string
11+
supportedVersions: string
12+
id: string
13+
locationOverride?: string
14+
}
15+
16+
export const defaultAmazonQWorkspaceLspConfig: LspConfig = {
17+
manifestUrl: 'https://aws-toolkit-language-servers.amazonaws.com/q-context/manifest.json',
18+
supportedVersions: '0.1.35',
19+
id: 'AmazonQ-Workspace', // used for identification in global storage/local disk location. Do not change.
20+
locationOverride: undefined,
21+
}
22+
23+
export function getAmazonQWorkspaceLspConfig(): LspConfig {
24+
return {
25+
...defaultAmazonQWorkspaceLspConfig,
26+
...(DevSettings.instance.getServiceConfig('amazonqWorkspaceLsp', {}) as LspConfig),
27+
...getServiceEnvVarConfig('amazonqWorkspaceLsp', Object.keys(defaultAmazonQWorkspaceLspConfig)),
28+
}
29+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { telemetry } from '../../shared/telemetry/telemetry'
1414
import { isCloud9 } from '../../shared/extensionUtilities'
1515
import globals, { isWeb } from '../../shared/extensionGlobals'
1616
import { isAmazonInternalOs } from '../../shared/vscode/env'
17-
import { WorkspaceLSPResolver } from './workspaceInstaller'
17+
import { WorkspaceLSPInstaller } from './workspaceInstaller'
1818
import { lspSetupStage } from '../../shared/lsp/utils/setupStage'
1919

2020
export interface Chunk {
@@ -186,7 +186,7 @@ export class LspController {
186186

187187
private async setupLsp(context: vscode.ExtensionContext) {
188188
await lspSetupStage('all', async () => {
189-
const installResult = await new WorkspaceLSPResolver().resolve()
189+
const installResult = await new WorkspaceLSPInstaller().resolve()
190190
await lspSetupStage('launch', async () => activateLsp(context, installResult.resourcePaths))
191191
this.logger.info('LspController: LSP activated')
192192
})

0 commit comments

Comments
 (0)