diff --git a/aws-toolkit-vscode.code-workspace b/aws-toolkit-vscode.code-workspace index f03cfefd48d..f03aafae2fe 100644 --- a/aws-toolkit-vscode.code-workspace +++ b/aws-toolkit-vscode.code-workspace @@ -1,19 +1,19 @@ { - "folders": [ - { - "path": "." - }, - { - "path": "packages/toolkit" - }, - { - "path": "packages/core" - }, - { - "path": "packages/amazonq" - } - ], - "settings": { - "typescript.tsdk": "node_modules/typescript/lib" - } -} \ No newline at end of file + "folders": [ + { + "path": ".", + }, + { + "path": "packages/toolkit", + }, + { + "path": "packages/core", + }, + { + "path": "packages/amazonq", + }, + ], + "settings": { + "typescript.tsdk": "node_modules/typescript/lib", + }, +} diff --git a/packages/amazonq/src/extension.ts b/packages/amazonq/src/extension.ts index fe5ce809c9d..fefe64a1bc4 100644 --- a/packages/amazonq/src/extension.ts +++ b/packages/amazonq/src/extension.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth' +import { Auth, AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth' import { activate as activateCodeWhisperer, shutdown as shutdownCodeWhisperer } from 'aws-core-vscode/codewhisperer' import { makeEndpointsProvider, registerGenericCommands } from 'aws-core-vscode' import { CommonAuthWebview } from 'aws-core-vscode/login' @@ -119,10 +119,10 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is } // This contains every lsp agnostic things (auth, security scan, code scan) await activateCodeWhisperer(extContext as ExtContext) - if (Experiments.instance.get('amazonqLSP', false)) { + if (Experiments.instance.get('amazonqLSP', false) || Auth.instance.isInternalAmazonUser()) { + // start the Amazon Q LSP for internal users first await activateAmazonqLsp(context) } - if (!Experiments.instance.get('amazonqLSPInline', false)) { await activateInlineCompletion() } diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index 84079ad3b77..733f29ad7be 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -6,11 +6,22 @@ import vscode, { env, version } from 'vscode' import * as nls from 'vscode-nls' import * as crypto from 'crypto' -import { LanguageClient, LanguageClientOptions } from 'vscode-languageclient' +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 } from '@aws/language-server-runtimes/protocol' +import { + ConnectionMetadata, + CreateFilesParams, + DeleteFilesParams, + DidChangeWorkspaceFoldersParams, + DidSaveTextDocumentParams, + GetConfigurationFromServerParams, + RenameFilesParams, + ResponseMessage, + updateConfigurationRequestType, + WorkspaceFolder, +} from '@aws/language-server-runtimes/protocol' import { Settings, oidcClientName, @@ -18,6 +29,7 @@ import { globals, Experiments, Commands, + oneSecond, validateNodeExe, getLogger, } from 'aws-core-vscode/shared' @@ -75,6 +87,9 @@ export async function startLanguageServer( window: { notifications: true, }, + q: { + developerProfiles: true, + }, }, }, credentials: { @@ -134,7 +149,27 @@ export async function startLanguageServer( activate(client, encryptionKey, resourcePaths.ui) } - const refreshInterval = auth.startTokenRefreshInterval() + const refreshInterval = auth.startTokenRefreshInterval(10 * oneSecond) + + const sendProfileToLsp = async () => { + try { + const result = await client.sendRequest(updateConfigurationRequestType.method, { + section: 'aws.q', + settings: { + profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn, + }, + }) + client.info( + `Client: Updated Amazon Q Profile ${AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn} to Amazon Q LSP`, + result + ) + } catch (err) { + client.error('Error when setting Q Developer Profile to Amazon Q LSP', err) + } + } + + // send profile to lsp once. + void sendProfileToLsp() toDispose.push( AuthUtil.instance.auth.onDidChangeActiveConnection(async () => { @@ -143,6 +178,62 @@ export async function startLanguageServer( AuthUtil.instance.auth.onDidDeleteConnection(async () => { client.sendNotification(notificationTypes.deleteBearerToken.method) }), + AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(sendProfileToLsp), + vscode.commands.registerCommand('aws.amazonq.getWorkspaceId', async () => { + const requestType = new RequestType( + 'aws/getConfigurationFromServer' + ) + const workspaceIdResp = await client.sendRequest(requestType.method, { + section: 'aws.q.workspaceContext', + }) + return workspaceIdResp + }), + vscode.workspace.onDidCreateFiles((e) => { + client.sendNotification('workspace/didCreateFiles', { + files: e.files.map((it) => { + return { uri: it.fsPath } + }), + } as CreateFilesParams) + }), + vscode.workspace.onDidDeleteFiles((e) => { + client.sendNotification('workspace/didDeleteFiles', { + files: e.files.map((it) => { + return { uri: it.fsPath } + }), + } as DeleteFilesParams) + }), + vscode.workspace.onDidRenameFiles((e) => { + client.sendNotification('workspace/didRenameFiles', { + files: e.files.map((it) => { + return { oldUri: it.oldUri.fsPath, newUri: it.newUri.fsPath } + }), + } as RenameFilesParams) + }), + vscode.workspace.onDidSaveTextDocument((e) => { + client.sendNotification('workspace/didSaveTextDocument', { + textDocument: { + uri: e.uri.fsPath, + }, + } as DidSaveTextDocumentParams) + }), + vscode.workspace.onDidChangeWorkspaceFolders((e) => { + client.sendNotification('workspace/didChangeWorkspaceFolder', { + event: { + added: e.added.map((it) => { + return { + name: it.name, + uri: it.uri.fsPath, + } as WorkspaceFolder + }), + removed: e.removed.map((it) => { + return { + name: it.name, + uri: it.uri.fsPath, + } as WorkspaceFolder + }), + }, + } as DidChangeWorkspaceFoldersParams) + }), { dispose: () => clearInterval(refreshInterval) } ) }) diff --git a/packages/amazonq/src/lsp/config.ts b/packages/amazonq/src/lsp/config.ts index fb1b43b8d48..a98afa44008 100644 --- a/packages/amazonq/src/lsp/config.ts +++ b/packages/amazonq/src/lsp/config.ts @@ -11,8 +11,8 @@ export interface ExtendedAmazonQLSPConfig extends LspConfig { } export const defaultAmazonQLspConfig: ExtendedAmazonQLSPConfig = { - manifestUrl: 'https://aws-toolkit-language-servers.amazonaws.com/codewhisperer/0/manifest.json', - supportedVersions: '^3.1.1', + manifestUrl: 'https://aws-toolkit-language-servers.amazonaws.com/remoteWorkspaceContext/0/manifest.json', + supportedVersions: '^1.0.0', id: 'AmazonQ', // used across IDEs for identifying global storage/local disk locations. Do not change. suppressPromptPrefix: 'amazonQ', path: undefined, diff --git a/packages/core/src/codewhisperer/util/editorContext.ts b/packages/core/src/codewhisperer/util/editorContext.ts index 4e2173a043e..459a521e572 100644 --- a/packages/core/src/codewhisperer/util/editorContext.ts +++ b/packages/core/src/codewhisperer/util/editorContext.ts @@ -18,6 +18,7 @@ import { checkLeftContextKeywordsForJson } from './commonUtil' import { CodeWhispererSupplementalContext } from '../models/model' import { getOptOutPreference } from '../../shared/telemetry/util' import { indent } from '../../shared/utilities/textUtilities' +import { isInDirectory } from '../../shared' import { AuthUtil } from './authUtil' let tabSize: number = getTabSizeSetting() @@ -83,6 +84,22 @@ export function getFileRelativePath(editor: vscode.TextEditor): string { return relativePath.substring(0, CodeWhispererConstants.filenameCharsLimit) } +async function getWorkspaceId(editor: vscode.TextEditor): Promise { + try { + const workspaceIds: { workspaces: { workspaceRoot: string; workspaceId: string }[] } = + await vscode.commands.executeCommand('aws.amazonq.getWorkspaceId') + for (const item of workspaceIds.workspaces) { + const path = vscode.Uri.parse(item.workspaceRoot).fsPath + if (isInDirectory(path, editor.document.uri.fsPath)) { + return item.workspaceId + } + } + } catch (err) { + getLogger().warn(`No workspace id found ${err}`) + } + return undefined +} + export async function buildListRecommendationRequest( editor: vscode.TextEditor, nextToken: string, @@ -121,6 +138,7 @@ export async function buildListRecommendationRequest( supplementalContexts: supplementalContext, customizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn, optOutPreference: getOptOutPreference(), + workspaceId: await getWorkspaceId(editor), profileArn: profile?.arn, }, supplementalMetadata: supplementalContexts,