Skip to content

Commit c89997e

Browse files
fix(amazonq): Sync client settings to language server (#7114)
When certain settings/configs are changed in the IDE, they need to be propagated to the language server. The settings were the customizationArn, the users opt in preference for telemetry, and some other things. This PR: - Pushes the new customization arn when it is changed. This arn only exists when the user is using the non-default customization, otherwise it is undefined. - A small hack needed to be made to explicitly push this change, since by default we utilized the automatic push to the LS when the users settings file changed (eg opt in telemetry). But because customization is not stored in the users settings, we didn't get that for free. See the command `aws.amazonq.updateCustomizations` which was made to handle this edge case. Which eventually **explicitly** uses the language server message `DidChangeConfigurationNotification` - Pushes the opt out telemetry setting to the language server - Since this setting is stored in the vscode settings json, the push to language server mechanism comes for free under the `DidChangeConfigurationNotification` message. - The Q Auth Profile ARN is a separate config that needed to be handled by a different handler (different from customization), so the function `pushConfigUpdate()` was created to route the correct config to the correct handler in the LS - This uses the message `updateConfigurationRequestType` which is different from `DidChangeConfigurationNotification` that the other configs use. This results in a different code path being hit, which is expected. Some helpful notes: - `getAmazonQRelatedWorkspaceConfigs()` (triggered through `DidChangeConfigurationNotification`) is the language server side code that parses the config sent by the client/ide. So the client/ide must match the shape expected by the LS. This is an example why `optOutTelemetry` did not work out of the box, and needed some changes to be consumable by the LS. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Signed-off-by: nkomonen-amazon <[email protected]> Co-authored-by: Josh Pinkney <[email protected]>
1 parent 3954302 commit c89997e

File tree

5 files changed

+80
-15
lines changed

5 files changed

+80
-15
lines changed

packages/amazonq/.vscode/launch.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"env": {
1515
"SSMDOCUMENT_LANGUAGESERVER_PORT": "6010",
1616
"WEBPACK_DEVELOPER_SERVER": "http://localhost:8080"
17+
// Below allows for overrides used during development
1718
// "__AMAZONQLSP_PATH": "${workspaceFolder}/../../../language-servers/app/aws-lsp-codewhisperer-runtimes/out/agent-standalone.js",
1819
// "__AMAZONQLSP_UI": "${workspaceFolder}/../../../language-servers/chat-client/build/amazonq-ui.js"
1920
},

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

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,23 @@ import { LanguageClient } from 'vscode-languageclient'
88
import { AmazonQChatViewProvider } from './webviewProvider'
99
import { registerCommands } from './commands'
1010
import { registerLanguageServerEventListener, registerMessageListeners } from './messages'
11-
import { getLogger, globals } from 'aws-core-vscode/shared'
11+
import { Commands, getLogger, globals, undefinedIfEmpty } from 'aws-core-vscode/shared'
1212
import { activate as registerLegacyChatListeners } from '../../app/chat/activation'
1313
import { DefaultAmazonQAppInitContext } from 'aws-core-vscode/amazonq'
14-
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
15-
import { updateConfigurationRequestType } from '@aws/language-server-runtimes/protocol'
14+
import { AuthUtil, getSelectedCustomization } from 'aws-core-vscode/codewhisperer'
15+
import {
16+
DidChangeConfigurationNotification,
17+
updateConfigurationRequestType,
18+
} from '@aws/language-server-runtimes/protocol'
1619

1720
export async function activate(languageClient: LanguageClient, encryptionKey: Buffer, mynahUIPath: string) {
1821
const disposables = globals.context.subscriptions
1922

2023
// Make sure we've sent an auth profile to the language server before even initializing the UI
21-
await updateProfile(languageClient)
24+
await pushConfigUpdate(languageClient, {
25+
type: 'profile',
26+
profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
27+
})
2228

2329
const provider = new AmazonQChatViewProvider(mynahUIPath)
2430

@@ -60,18 +66,48 @@ export async function activate(languageClient: LanguageClient, encryptionKey: Bu
6066

6167
disposables.push(
6268
AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(async () => {
63-
void updateProfile(languageClient)
69+
void pushConfigUpdate(languageClient, {
70+
type: 'profile',
71+
profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
72+
})
6473
await provider.refreshWebview()
74+
}),
75+
Commands.register('aws.amazonq.updateCustomizations', () => {
76+
void pushConfigUpdate(languageClient, {
77+
type: 'customization',
78+
customization: undefinedIfEmpty(getSelectedCustomization().arn),
79+
})
6580
})
6681
)
6782
}
6883

69-
async function updateProfile(client: LanguageClient) {
70-
// update the profile on the language server
71-
await client.sendRequest(updateConfigurationRequestType.method, {
72-
section: 'aws.q',
73-
settings: {
74-
profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
75-
},
76-
})
84+
/**
85+
* Push a config value to the language server, effectively updating it with the
86+
* latest configuration from the client.
87+
*
88+
* The issue is we need to push certain configs to different places, since there are
89+
* different handlers for specific configs. So this determines the correct place to
90+
* push the given config.
91+
*/
92+
async function pushConfigUpdate(client: LanguageClient, config: QConfigs) {
93+
if (config.type === 'profile') {
94+
await client.sendRequest(updateConfigurationRequestType.method, {
95+
section: 'aws.q',
96+
settings: { profileArn: config.profileArn },
97+
})
98+
} else if (config.type === 'customization') {
99+
client.sendNotification(DidChangeConfigurationNotification.type.method, {
100+
section: 'aws.q',
101+
settings: { customization: config.customization },
102+
})
103+
}
104+
}
105+
type ProfileConfig = {
106+
type: 'profile'
107+
profileArn: string | undefined
108+
}
109+
type CustomizationConfig = {
110+
type: 'customization'
111+
customization: string | undefined
77112
}
113+
type QConfigs = ProfileConfig | CustomizationConfig

packages/amazonq/src/lsp/client.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import * as crypto from 'crypto'
99
import { LanguageClient, LanguageClientOptions, RequestType } from 'vscode-languageclient'
1010
import { InlineCompletionManager } from '../app/inline/completion'
1111
import { AmazonQLspAuth, encryptionKey, notificationTypes } from './auth'
12-
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
1312
import {
1413
ConnectionMetadata,
1514
CreateFilesParams,
@@ -22,6 +21,7 @@ import {
2221
updateConfigurationRequestType,
2322
WorkspaceFolder,
2423
} from '@aws/language-server-runtimes/protocol'
24+
import { AuthUtil, getSelectedCustomization } from 'aws-core-vscode/codewhisperer'
2525
import {
2626
Settings,
2727
oidcClientName,
@@ -32,6 +32,8 @@ import {
3232
oneSecond,
3333
validateNodeExe,
3434
getLogger,
35+
undefinedIfEmpty,
36+
getOptOutPreference,
3537
} from 'aws-core-vscode/shared'
3638
import { activate } from './chat/activation'
3739
import { AmazonQResourcePaths } from './lspInstaller'
@@ -74,17 +76,40 @@ export async function startLanguageServer(
7476
documentSelector,
7577
middleware: {
7678
workspace: {
79+
/**
80+
* Convert VSCode settings format to be compatible with flare's configs
81+
*/
7782
configuration: async (params, token, next) => {
7883
const config = await next(params, token)
7984
if (params.items[0].section === 'aws.q') {
85+
const customization = undefinedIfEmpty(getSelectedCustomization().arn)
86+
/**
87+
* IMPORTANT: This object is parsed by the following code in the language server, **so
88+
* it must match that expected shape**.
89+
* https://github.com/aws/language-servers/blob/1d2ca018f2248106690438b860d40a7ee67ac728/server/aws-lsp-codewhisperer/src/shared/amazonQServiceManager/configurationUtils.ts#L114
90+
*/
8091
return [
8192
{
93+
customization,
94+
optOutTelemetry: getOptOutPreference() === 'OPTOUT',
8295
projectContext: {
8396
enableLocalIndexing: true,
8497
},
8598
},
8699
]
87100
}
101+
if (params.items[0].section === 'aws.codeWhisperer') {
102+
return [
103+
{
104+
includeSuggestionsWithCodeReferences: vscode.workspace
105+
.getConfiguration()
106+
.get('amazonQ.showCodeWithReferences'),
107+
shareCodeWhispererContentWithAWS: vscode.workspace
108+
.getConfiguration()
109+
.get('amazonQ.shareContentWithAWS'),
110+
},
111+
]
112+
}
88113
return config
89114
},
90115
},

packages/core/src/codewhisperer/util/customizationUtil.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ export const setSelectedCustomization = async (customization: Customization, isO
147147
}
148148
vsCodeState.isFreeTierLimitReached = false
149149
await Commands.tryExecute('aws.amazonq.refreshStatusBar')
150+
151+
// hack: triggers amazon q to send the customizations back to flare
152+
await Commands.tryExecute('aws.amazonq.updateCustomizations')
150153
}
151154

152155
export const getPersistedCustomizations = (): Customization[] => {

packages/core/src/shared/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export { Prompter } from './ui/prompter'
2727
export { VirtualFileSystem } from './virtualFilesystem'
2828
export { VirtualMemoryFile } from './virtualMemoryFile'
2929
export { AmazonqCreateUpload, Metric } from './telemetry/telemetry'
30-
export { getClientId, getOperatingSystem } from './telemetry/util'
30+
export { getClientId, getOperatingSystem, getOptOutPreference } from './telemetry/util'
3131
export { extensionVersion } from './vscode/env'
3232
export { cast } from './utilities/typeConstructors'
3333
export * as workspaceUtils from './utilities/workspaceUtils'

0 commit comments

Comments
 (0)