Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/amazonq/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"env": {
"SSMDOCUMENT_LANGUAGESERVER_PORT": "6010",
"WEBPACK_DEVELOPER_SERVER": "http://localhost:8080"
// Below allows for overrides used during development
// "__AMAZONQLSP_PATH": "${workspaceFolder}/../../../language-servers/app/aws-lsp-codewhisperer-runtimes/out/agent-standalone.js",
// "__AMAZONQLSP_UI": "${workspaceFolder}/../../../language-servers/chat-client/build/amazonq-ui.js"
},
Expand Down
62 changes: 49 additions & 13 deletions packages/amazonq/src/lsp/chat/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,23 @@ import { LanguageClient } from 'vscode-languageclient'
import { AmazonQChatViewProvider } from './webviewProvider'
import { registerCommands } from './commands'
import { registerLanguageServerEventListener, registerMessageListeners } from './messages'
import { getLogger, globals } from 'aws-core-vscode/shared'
import { Commands, getLogger, globals, undefinedIfEmpty } from 'aws-core-vscode/shared'
import { activate as registerLegacyChatListeners } from '../../app/chat/activation'
import { DefaultAmazonQAppInitContext } from 'aws-core-vscode/amazonq'
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
import { updateConfigurationRequestType } from '@aws/language-server-runtimes/protocol'
import { AuthUtil, getSelectedCustomization } from 'aws-core-vscode/codewhisperer'
import {
DidChangeConfigurationNotification,
updateConfigurationRequestType,
} from '@aws/language-server-runtimes/protocol'

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

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

const provider = new AmazonQChatViewProvider(mynahUIPath)

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

disposables.push(
AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(async () => {
void updateProfile(languageClient)
void pushConfigUpdate(languageClient, {
type: 'profile',
profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
})
await provider.refreshWebview()
}),
Commands.register('aws.amazonq.updateCustomizations', () => {
void pushConfigUpdate(languageClient, {
type: 'customization',
customization: undefinedIfEmpty(getSelectedCustomization().arn),
})
})
)
}

async function updateProfile(client: LanguageClient) {
// update the profile on the language server
await client.sendRequest(updateConfigurationRequestType.method, {
section: 'aws.q',
settings: {
profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
},
})
/**
* Push a config value to the language server, effectively updating it with the
* latest configuration from the client.
*
* The issue is we need to push certain configs to different places, since there are
* different handlers for specific configs. So this determines the correct place to
* push the given config.
*/
async function pushConfigUpdate(client: LanguageClient, config: QConfigs) {
if (config.type === 'profile') {
await client.sendRequest(updateConfigurationRequestType.method, {
section: 'aws.q',
settings: { profileArn: config.profileArn },
})
} else if (config.type === 'customization') {
client.sendNotification(DidChangeConfigurationNotification.type.method, {
section: 'aws.q',
settings: { customization: config.customization },
})
}
}
type ProfileConfig = {
type: 'profile'
profileArn: string | undefined
}
type CustomizationConfig = {
type: 'customization'
customization: string | undefined
}
type QConfigs = ProfileConfig | CustomizationConfig
27 changes: 26 additions & 1 deletion packages/amazonq/src/lsp/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import * as crypto from 'crypto'
import { LanguageClient, LanguageClientOptions, RequestType } from 'vscode-languageclient'
import { InlineCompletionManager } from '../app/inline/completion'
import { AmazonQLspAuth, encryptionKey, notificationTypes } from './auth'
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
import {
ConnectionMetadata,
CreateFilesParams,
Expand All @@ -22,6 +21,7 @@ import {
updateConfigurationRequestType,
WorkspaceFolder,
} from '@aws/language-server-runtimes/protocol'
import { AuthUtil, getSelectedCustomization } from 'aws-core-vscode/codewhisperer'
import {
Settings,
oidcClientName,
Expand All @@ -32,6 +32,8 @@ import {
oneSecond,
validateNodeExe,
getLogger,
undefinedIfEmpty,
getOptOutPreference,
} from 'aws-core-vscode/shared'
import { activate } from './chat/activation'
import { AmazonQResourcePaths } from './lspInstaller'
Expand Down Expand Up @@ -74,17 +76,40 @@ export async function startLanguageServer(
documentSelector,
middleware: {
workspace: {
/**
* Convert VSCode settings format to be compatible with flare's configs
*/
configuration: async (params, token, next) => {
const config = await next(params, token)
if (params.items[0].section === 'aws.q') {
const customization = undefinedIfEmpty(getSelectedCustomization().arn)
/**
* IMPORTANT: This object is parsed by the following code in the language server, **so
* it must match that expected shape**.
* https://github.com/aws/language-servers/blob/1d2ca018f2248106690438b860d40a7ee67ac728/server/aws-lsp-codewhisperer/src/shared/amazonQServiceManager/configurationUtils.ts#L114
*/
return [
{
customization,
optOutTelemetry: getOptOutPreference() === 'OPTOUT',
Copy link
Contributor

Choose a reason for hiding this comment

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

oh wait this is a boolean? the type they have in the interface vs what is expected to be passed in is so confusing 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

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

projectContext: {
enableLocalIndexing: true,
},
},
]
}
if (params.items[0].section === 'aws.codeWhisperer') {
return [
{
includeSuggestionsWithCodeReferences: vscode.workspace
.getConfiguration()
.get('amazonQ.showCodeWithReferences'),
shareCodeWhispererContentWithAWS: vscode.workspace
.getConfiguration()
.get('amazonQ.shareContentWithAWS'),
},
]
}
return config
},
},
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/codewhisperer/util/customizationUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ export const setSelectedCustomization = async (customization: Customization, isO
}
vsCodeState.isFreeTierLimitReached = false
await Commands.tryExecute('aws.amazonq.refreshStatusBar')

// hack: triggers amazon q to send the customizations back to flare
await Commands.tryExecute('aws.amazonq.updateCustomizations')
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not call a function instead of a command?

Commands should be avoided, because they are less reliable and less visible if they fail (partly because they tend to be async).

}

export const getPersistedCustomizations = (): Customization[] => {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export { Prompter } from './ui/prompter'
export { VirtualFileSystem } from './virtualFilesystem'
export { VirtualMemoryFile } from './virtualMemoryFile'
export { AmazonqCreateUpload, Metric } from './telemetry/telemetry'
export { getClientId, getOperatingSystem } from './telemetry/util'
export { getClientId, getOperatingSystem, getOptOutPreference } from './telemetry/util'
export { extensionVersion } from './vscode/env'
export { cast } from './utilities/typeConstructors'
export * as workspaceUtils from './utilities/workspaceUtils'
Expand Down
Loading