diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 087d6e838ab..af930a3a34d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -368,31 +368,28 @@ The `aws.dev.forceDevMode` setting enables or disables Toolkit "dev mode". Witho ``` tail -F ~/awstoolkit.log ``` -- Use the `AWS (Developer): Watch Logs` command to watch and filter Toolkit logs (including - telemetry) in VSCode. - - Only available if you enabled "dev mode" (`aws.dev.forceDevMode` setting, see above). - - Enter text in the Debug Console filter box to show only log messages with that text.
- VSCode Debug Console +- Use the Output panel to watch and filter Toolkit logs (including telemetry) in VSCode. + - Enter text in the Output panel filter box to show only log messages with that text. #### Enabling Debug Logs How to enable more detailed debug logs in the extensions. If you need to report an issue attach these to give the most detailed information. -1. Open the Command Palette (`cmd/ctrl` + `shift` + `p`), then search for "View Logs". Choose the correct option for the extension you want, eg: `AWS: View Logs` or `Amazon Q: View Logs` - ![](./docs/images/logsView.png) +1. Open the Command Palette (`cmd/ctrl` + `shift` + `p`), then search for "View Logs". Choose either `AWS: View Logs` or `Amazon Q: View Logs`. + - ![](./docs/images/logsView.png) 2. Click the gear icon on the bottom right and select `Debug` - ![](./docs/images/logsSetDebug.png) + - ![](./docs/images/logsSetDebug.png) 3. Click the gear icon again and select `Set As Default`. This will ensure we stay in `Debug` until explicitly changed - ![](./docs/images/logsSetDefault.png) + - ![](./docs/images/logsSetDefault.png) 4. Open the Command Palette again and select `Reload Window`. 5. Now you should see additional `[debug]` prefixed logs in the output. - ![](./docs/images/logsDebugLog.png) + - ![](./docs/images/logsDebugLog.png) ### Telemetry - See [docs/telemetry.md](./docs/telemetry.md) for guidelines on developing telemetry in this project. -- To watch Toolkit telemetry events, use the `AWS (Developer): Watch Logs` command (see [Logging](#logging) above) and enter "telemetry" in the Debug Console filter box. +- To watch Toolkit telemetry events, use the `Amazon Q: View Logs` command (see [Logging](#logging) above) and enter "telemetry" in the filter box. ### Service Endpoints diff --git a/docs/images/debug-console-filter.png b/docs/images/debug-console-filter.png deleted file mode 100644 index 06703ab4149..00000000000 Binary files a/docs/images/debug-console-filter.png and /dev/null differ diff --git a/packages/amazonq/.changes/next-release/Bug Fix-704d175a-a99f-4ce7-bf08-b94d6f7218c0.json b/packages/amazonq/.changes/next-release/Bug Fix-704d175a-a99f-4ce7-bf08-b94d6f7218c0.json new file mode 100644 index 00000000000..01d6ec7c8b0 --- /dev/null +++ b/packages/amazonq/.changes/next-release/Bug Fix-704d175a-a99f-4ce7-bf08-b94d6f7218c0.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "`Send to prompt` and other context menu options not sent if chat was closed" +} diff --git a/packages/amazonq/.changes/next-release/Feature-d86356a0-bc42-4187-a7f8-205afbc25d76.json b/packages/amazonq/.changes/next-release/Feature-d86356a0-bc42-4187-a7f8-205afbc25d76.json new file mode 100644 index 00000000000..510dd1c150d --- /dev/null +++ b/packages/amazonq/.changes/next-release/Feature-d86356a0-bc42-4187-a7f8-205afbc25d76.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "Amazon Q /dev: support `.hbs`, `.gjs`, `.gts`, `.astro`, `.mdx`, `.svelte`, `.erb`, `.rake` files" +} diff --git a/packages/amazonq/.changes/next-release/Feature-d9128dff-2867-4bf4-9046-2a52a36803d7.json b/packages/amazonq/.changes/next-release/Feature-d9128dff-2867-4bf4-9046-2a52a36803d7.json new file mode 100644 index 00000000000..add00c79b5e --- /dev/null +++ b/packages/amazonq/.changes/next-release/Feature-d9128dff-2867-4bf4-9046-2a52a36803d7.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "/transform: automatically download results when ready" +} diff --git a/packages/core/src/amazonq/apps/initContext.ts b/packages/core/src/amazonq/apps/initContext.ts index e33fafca7c3..f8b06e4b46a 100644 --- a/packages/core/src/amazonq/apps/initContext.ts +++ b/packages/core/src/amazonq/apps/initContext.ts @@ -4,20 +4,20 @@ */ import { EventEmitter } from 'vscode' -import { MessagePublisher } from '../messages/messagePublisher' +import { MessagePublisher, UiMessagePublisher } from '../messages/messagePublisher' import { MessageListener } from '../messages/messageListener' import { TabType } from '../webview/ui/storages/tabsStorage' export interface AmazonQAppInitContext { registerWebViewToAppMessagePublisher(eventEmitter: MessagePublisher, tabType: TabType): void - getAppsToWebViewMessagePublisher(): MessagePublisher + getAppsToWebViewMessagePublisher(): UiMessagePublisher onDidChangeAmazonQVisibility: EventEmitter } export class DefaultAmazonQAppInitContext implements AmazonQAppInitContext { private readonly appsToWebViewEventEmitter = new EventEmitter() private readonly appsToWebViewMessageListener = new MessageListener(this.appsToWebViewEventEmitter) - private readonly appsToWebViewMessagePublisher = new MessagePublisher(this.appsToWebViewEventEmitter) + private readonly appsToWebViewMessagePublisher = new UiMessagePublisher(this.appsToWebViewEventEmitter) private readonly webViewToAppsMessagePublishers: Map> = new Map() public readonly onDidChangeAmazonQVisibility = new EventEmitter() @@ -41,7 +41,7 @@ export class DefaultAmazonQAppInitContext implements AmazonQAppInitContext { return this.appsToWebViewMessageListener } - getAppsToWebViewMessagePublisher(): MessagePublisher { + getAppsToWebViewMessagePublisher(): UiMessagePublisher { return this.appsToWebViewMessagePublisher } } diff --git a/packages/core/src/amazonq/messages/messagePublisher.ts b/packages/core/src/amazonq/messages/messagePublisher.ts index d2987a99536..7fa6976b9a9 100644 --- a/packages/core/src/amazonq/messages/messagePublisher.ts +++ b/packages/core/src/amazonq/messages/messagePublisher.ts @@ -12,3 +12,48 @@ export class MessagePublisher { this.eventEmitter.fire(event) } } + +/** + * Same as {@link MessagePublisher}, but will wait until the UI indicates it + * is ready to recieve messages, before the message is published. + * + * This solves a problem when running a right click menu option like + * "Send To Prompt" BUT chat is not opened yet, it would result in the prompt failing to + * be recieved by chat. + */ +export class UiMessagePublisher extends MessagePublisher { + private isUiReady: boolean = false + private buffer: T[] = [] + + constructor(eventEmitter: EventEmitter) { + super(eventEmitter) + } + + public override publish(event: T): void { + // immediately send if Chat UI is ready + if (this.isUiReady) { + super.publish(event) + return + } + + this.buffer.push(event) + } + + /** + * Indicate the Q Chat UI is ready to recieve messages. + */ + public setUiReady() { + this.isUiReady = true + this.flush() + } + + /** + * Publishes all blocked messages + */ + private flush() { + for (const msg of this.buffer) { + super.publish(msg) + } + this.buffer = [] + } +} diff --git a/packages/core/src/amazonq/webview/messages/messageDispatcher.ts b/packages/core/src/amazonq/webview/messages/messageDispatcher.ts index 8acd04e8953..fdd1584b767 100644 --- a/packages/core/src/amazonq/webview/messages/messageDispatcher.ts +++ b/packages/core/src/amazonq/webview/messages/messageDispatcher.ts @@ -13,6 +13,7 @@ import { telemetry } from '../../../shared/telemetry' import { AmazonQChatMessageDuration } from '../../messages/chatMessageDuration' import { globals, openUrl } from '../../../shared' import { isClickTelemetry, isOpenAgentTelemetry } from '../ui/telemetry/actions' +import { DefaultAmazonQAppInitContext } from '../../apps/initContext' export function dispatchWebViewMessagesToApps( webview: Webview, @@ -21,12 +22,12 @@ export function dispatchWebViewMessagesToApps( webview.onDidReceiveMessage((msg) => { switch (msg.command) { case 'ui-is-ready': { + DefaultAmazonQAppInitContext.instance.getAppsToWebViewMessagePublisher().setUiReady() /** * ui-is-ready isn't associated to any tab so just record the telemetry event and continue. * This would be equivalent of the duration between "user clicked open q" and "ui has become available" * NOTE: Amazon Q UI is only loaded ONCE. The state is saved between each hide/show of the webview. */ - telemetry.webview_load.emit({ webviewName: 'amazonq', duration: performance.measure(amazonqMark.uiReady, amazonqMark.open).duration, diff --git a/packages/core/src/codewhisperer/commands/startTransformByQ.ts b/packages/core/src/codewhisperer/commands/startTransformByQ.ts index 50be99b7fcf..cec513d4f58 100644 --- a/packages/core/src/codewhisperer/commands/startTransformByQ.ts +++ b/packages/core/src/codewhisperer/commands/startTransformByQ.ts @@ -775,6 +775,12 @@ export async function postTransformationJob() { if (transformByQState.getPayloadFilePath() !== '') { fs.rmSync(transformByQState.getPayloadFilePath(), { recursive: true, force: true }) // delete ZIP if it exists } + + // attempt download for user + // TODO: refactor as explained here https://github.com/aws/aws-toolkit-vscode/pull/6519/files#r1946873107 + if (transformByQState.isSucceeded() || transformByQState.isPartiallySucceeded()) { + await vscode.commands.executeCommand('aws.amazonq.transformationHub.reviewChanges.startReview') + } } export async function transformationJobErrorHandler(error: any) { diff --git a/packages/core/src/dev/activation.ts b/packages/core/src/dev/activation.ts index 0033eaa8548..15baf1bc294 100644 --- a/packages/core/src/dev/activation.ts +++ b/packages/core/src/dev/activation.ts @@ -199,20 +199,6 @@ export async function activate(ctx: vscode.ExtensionContext): Promise { .filter((e) => (opts.menuOptions ?? Object.keys(options)).includes(e[0])) .map((e) => e[1]) ) - }), - // "AWS (Developer): Watch Logs" - Commands.register('aws.dev.viewLogs', async () => { - // HACK: Use startDebugging() so we can use the DEBUG CONSOLE (which supports - // user-defined filtering, unlike the OUTPUT panel). - await vscode.debug.startDebugging(undefined, { - name: 'aws-dev-log', - request: 'launch', - type: 'node', // Nonsense, to force the debugger to start. - }) - getLogger().enableDebugConsole() - if (!getLogger().logLevelEnabled('debug')) { - getLogger().setLogLevel('debug') - } }) ) diff --git a/packages/core/src/shared/filetypes.ts b/packages/core/src/shared/filetypes.ts index 843397e6a17..9457592c8b2 100644 --- a/packages/core/src/shared/filetypes.ts +++ b/packages/core/src/shared/filetypes.ts @@ -155,6 +155,7 @@ export const codefileExtensions = new Set([ '.ads', '.apl', '.asm', + '.astro', '.awk', '.b', '.bas', @@ -195,6 +196,7 @@ export const codefileExtensions = new Set([ '.el', '.elm', '.env', + '.erb', '.erl', '.ex', '.exs', @@ -211,6 +213,7 @@ export const codefileExtensions = new Set([ '.fsx', '.gd', '.gitignore', + '.gjs', '.go', '.gql', '.gradle', @@ -220,9 +223,11 @@ export const codefileExtensions = new Set([ '.gsp', '.gst', '.gsx', + '.gts', '.gvy', '.h', '.hack', + '.hbs', '.hh', '.hpp', '.hrl', @@ -253,6 +258,7 @@ export const codefileExtensions = new Set([ '.mak', '.makefile', '.md', + '.mdx', '.mjs', '.ml', '.mli', @@ -289,6 +295,7 @@ export const codefileExtensions = new Set([ '.pyw', '.qs', '.r', + '.rake', '.raku', '.rakumod', '.rakutest', @@ -331,6 +338,7 @@ export const codefileExtensions = new Set([ '.ss', '.st', '.sv', + '.svelte', '.svg', '.swift', '.t', diff --git a/packages/core/src/shared/logger/logger.ts b/packages/core/src/shared/logger/logger.ts index 98a7e29d48e..ccc5e912d27 100644 --- a/packages/core/src/shared/logger/logger.ts +++ b/packages/core/src/shared/logger/logger.ts @@ -5,7 +5,15 @@ import * as vscode from 'vscode' -export type LogTopic = 'crashMonitoring' | 'dev/beta' | 'notifications' | 'test' | 'childProcess' | 'lsp' | 'unknown' +export type LogTopic = + | 'crashMonitoring' + | 'dev/beta' + | 'notifications' + | 'test' + | 'childProcess' + | 'unknown' + | 'chat' + | 'lsp' class ErrorLog { constructor( @@ -35,8 +43,6 @@ export interface Logger { /** Returns true if the given log level is being logged. */ logLevelEnabled(logLevel: LogLevel): boolean getLogById(logID: number, file: vscode.Uri): string | undefined - /** HACK: Enables logging to vscode Debug Console. */ - enableDebugConsole(): void sendToLog( logLevel: 'debug' | 'verbose' | 'info' | 'warn' | 'error', message: string | Error, @@ -74,8 +80,6 @@ export abstract class BaseLogger implements Logger { abstract setLogLevel(logLevel: LogLevel): void abstract logLevelEnabled(logLevel: LogLevel): boolean abstract getLogById(logID: number, file: vscode.Uri): string | undefined - /** HACK: Enables logging to vscode Debug Console. */ - abstract enableDebugConsole(): void } /** @@ -166,7 +170,6 @@ export class NullLogger extends BaseLogger { public getLogById(logID: number, file: vscode.Uri): string | undefined { return undefined } - public enableDebugConsole(): void {} override sendToLog( logLevel: 'error' | 'warn' | 'info' | 'verbose' | 'debug', message: string | Error, @@ -190,7 +193,6 @@ export class ConsoleLogger extends BaseLogger { public getLogById(logID: number, file: vscode.Uri): string | undefined { return undefined } - public enableDebugConsole(): void {} override sendToLog( logLevel: 'error' | 'warn' | 'info' | 'verbose' | 'debug', message: string | Error, @@ -244,10 +246,6 @@ export class TopicLogger extends BaseLogger implements vscode.Disposable { return this.logger.getLogById(logID, file) } - override enableDebugConsole(): void { - this.logger.enableDebugConsole() - } - override sendToLog(level: LogLevel, message: string | Error, ...meta: any[]): number { if (typeof message === 'string') { message = prependTopic(this.topic, message) as string diff --git a/packages/core/src/shared/logger/toolkitLogger.ts b/packages/core/src/shared/logger/toolkitLogger.ts index d525fb7cf20..0fdf58dc939 100644 --- a/packages/core/src/shared/logger/toolkitLogger.ts +++ b/packages/core/src/shared/logger/toolkitLogger.ts @@ -48,10 +48,6 @@ export class ToolkitLogger extends BaseLogger implements vscode.Disposable { }) } - public enableDebugConsole(): void { - this.logToConsole() - } - public setLogLevel(logLevel: LogLevel) { if (this.logger.level === logLevel) { return diff --git a/packages/core/src/shared/lsp/utils/platform.ts b/packages/core/src/shared/lsp/utils/platform.ts index 44e68c423d2..4d242c3833e 100644 --- a/packages/core/src/shared/lsp/utils/platform.ts +++ b/packages/core/src/shared/lsp/utils/platform.ts @@ -36,8 +36,11 @@ export function createServerOptions({ execArgv: string[] }) { return async () => { - const debugArgs = isDebugInstance() ? '--inspect=6080' : '' - const lspProcess = new ChildProcess(executable, [debugArgs, serverModule, ...execArgv]) + const args = [serverModule, ...execArgv] + if (isDebugInstance()) { + args.unshift('--inspect=6080') + } + const lspProcess = new ChildProcess(executable, args) // this is a long running process, awaiting it will never resolve void lspProcess.run() diff --git a/packages/core/src/shared/regions/regionProvider.ts b/packages/core/src/shared/regions/regionProvider.ts index 00d13942196..426e27d73c5 100644 --- a/packages/core/src/shared/regions/regionProvider.ts +++ b/packages/core/src/shared/regions/regionProvider.ts @@ -162,25 +162,30 @@ export class RegionProvider { remote: () => Endpoints | Promise }): RegionProvider { const instance = new this() + void instance.init(endpointsProvider) + return instance + } - async function load() { - getLogger().info('endpoints: retrieving AWS endpoints data') - instance.loadFromEndpoints(await endpointsProvider.local()) - - try { - instance.loadFromEndpoints(await endpointsProvider.remote()) - } catch (err) { - getLogger().warn( - `endpoints: failed to load from remote source, region data may appear outdated: %s`, - err - ) - } + async init(endpointsProvider: { + local: () => Endpoints | Promise + remote: () => Endpoints | Promise + }) { + getLogger().info('endpoints: retrieving AWS endpoints data') + + try { + this.loadFromEndpoints(await endpointsProvider.local()) + } catch (err) { + getLogger().warn(`endpoints: failed to load from local source: %s`, err) } - load().catch((err) => { - getLogger().error('Failure while loading Endpoints Manifest: %s', err) + try { + this.loadFromEndpoints(await endpointsProvider.remote()) + } catch (err) { + getLogger().warn(`endpoints: failed to load from remote source, region data may appear outdated: %s`, err) + } - return vscode.window.showErrorMessage( + if (this.getRegions().length === 0) { + void vscode.window.showErrorMessage( `${localize( 'AWS.error.endpoint.load.failure', 'The {0} Toolkit was unable to load endpoints data.', @@ -190,9 +195,7 @@ export class RegionProvider { 'Toolkit functionality may be impacted until VS Code is restarted.' )}` ) - }) - - return instance + } } } diff --git a/packages/core/src/test/testLogger.ts b/packages/core/src/test/testLogger.ts index db9d460652c..a2970c37d8b 100644 --- a/packages/core/src/test/testLogger.ts +++ b/packages/core/src/test/testLogger.ts @@ -23,8 +23,6 @@ export class TestLogger extends BaseLogger { super() } - public enableDebugConsole(): void {} - public getLoggedEntries(...logLevels: LogLevel[]): Loggable[] { return this.loggedEntries .filter((loggedEntry) => logLevels.length === 0 || logLevels.includes(loggedEntry.logLevel)) diff --git a/packages/core/src/testInteg/regionProvider.test.ts b/packages/core/src/testInteg/regionProvider.test.ts new file mode 100644 index 00000000000..a3486c2bac0 --- /dev/null +++ b/packages/core/src/testInteg/regionProvider.test.ts @@ -0,0 +1,37 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import assert from 'assert' +import { makeEndpointsProvider } from '../extension' +import { RegionProvider } from '../shared/regions/regionProvider' +import globals from '../shared/extensionGlobals' +import path from 'path' +import { TestFolder } from '../test/testUtil' + +describe('Region Provider', async function () { + let tempFolder: TestFolder + + before(async () => { + tempFolder = await TestFolder.create() + }) + + it('resolves from remote', async function () { + /** + * Make sure the local file doesn't resolve to any endpoints. + * That way we can make sure remote contents are fetched + */ + await tempFolder.write('foo.json', '{}') + globals.manifestPaths.endpoints = path.join(tempFolder.path, 'foo.json') + + await assert.doesNotReject(async () => { + const endpointProvider = makeEndpointsProvider() + const regionProvider = new RegionProvider() + await regionProvider.init(endpointProvider) + + // regions loaded from the remote + assert.ok(regionProvider.getRegions().length > 0) + }) + }) +}) diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index 21dbc06f185..e152c8a2c9f 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -3895,11 +3895,6 @@ "category": "AWS (Developer)", "enablement": "aws.isDevMode" }, - { - "command": "aws.dev.viewLogs", - "title": "Watch Logs", - "category": "AWS (Developer)" - }, { "command": "aws.openInApplicationComposerDialog", "title": "%AWS.command.applicationComposer.openDialog%",