diff --git a/packages/amazonq/.changes/next-release/Removal-083c6e6d-e543-4df7-86ae-0c0f8c0a27ee.json b/packages/amazonq/.changes/next-release/Removal-083c6e6d-e543-4df7-86ae-0c0f8c0a27ee.json new file mode 100644 index 00000000000..1eea2e746fe --- /dev/null +++ b/packages/amazonq/.changes/next-release/Removal-083c6e6d-e543-4df7-86ae-0c0f8c0a27ee.json @@ -0,0 +1,4 @@ +{ + "type": "Removal", + "description": "Deprecate \"amazon q is generating...\" UI for inline suggestion" +} diff --git a/packages/amazonq/src/app/inline/completion.ts b/packages/amazonq/src/app/inline/completion.ts index 721c5e34b52..30bcf83144c 100644 --- a/packages/amazonq/src/app/inline/completion.ts +++ b/packages/amazonq/src/app/inline/completion.ts @@ -36,7 +36,6 @@ import { noInlineSuggestionsMsg, ReferenceInlineProvider, } from 'aws-core-vscode/codewhisperer' -import { InlineGeneratingMessage } from './inlineGeneratingMessage' import { LineTracker } from './stateTracker/lineTracker' import { InlineTutorialAnnotation } from './tutorials/inlineTutorialAnnotation' import { TelemetryHelper } from './telemetryHelper' @@ -55,7 +54,7 @@ export class InlineCompletionManager implements Disposable { private sessionManager: SessionManager private recommendationService: RecommendationService private lineTracker: LineTracker - private incomingGeneratingMessage: InlineGeneratingMessage + private inlineTutorialAnnotation: InlineTutorialAnnotation private readonly logSessionResultMessageName = 'aws/logInlineCompletionSessionResults' private documentChangeListener: Disposable @@ -70,12 +69,7 @@ export class InlineCompletionManager implements Disposable { this.languageClient = languageClient this.sessionManager = sessionManager this.lineTracker = lineTracker - this.incomingGeneratingMessage = new InlineGeneratingMessage(this.lineTracker) - this.recommendationService = new RecommendationService( - this.sessionManager, - this.incomingGeneratingMessage, - cursorUpdateRecorder - ) + this.recommendationService = new RecommendationService(this.sessionManager, cursorUpdateRecorder) this.inlineTutorialAnnotation = inlineTutorialAnnotation this.inlineCompletionProvider = new AmazonQInlineCompletionItemProvider( languageClient, @@ -105,7 +99,6 @@ export class InlineCompletionManager implements Disposable { public dispose(): void { if (this.disposable) { this.disposable.dispose() - this.incomingGeneratingMessage.dispose() this.lineTracker.dispose() } if (this.documentChangeListener) { diff --git a/packages/amazonq/src/app/inline/inlineGeneratingMessage.ts b/packages/amazonq/src/app/inline/inlineGeneratingMessage.ts deleted file mode 100644 index 6c2d97fdad2..00000000000 --- a/packages/amazonq/src/app/inline/inlineGeneratingMessage.ts +++ /dev/null @@ -1,98 +0,0 @@ -/*! - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -import { editorUtilities } from 'aws-core-vscode/shared' -import * as vscode from 'vscode' -import { LineSelection, LineTracker } from './stateTracker/lineTracker' -import { AuthUtil } from 'aws-core-vscode/codewhisperer' -import { cancellableDebounce } from 'aws-core-vscode/utils' - -/** - * Manages the inline ghost text message show when Inline Suggestions is "thinking". - */ -export class InlineGeneratingMessage implements vscode.Disposable { - private readonly _disposable: vscode.Disposable - - private readonly cwLineHintDecoration: vscode.TextEditorDecorationType = - vscode.window.createTextEditorDecorationType({ - after: { - margin: '0 0 0 3em', - contentText: 'Amazon Q is generating...', - textDecoration: 'none', - fontWeight: 'normal', - fontStyle: 'normal', - color: 'var(--vscode-editorCodeLens-foreground)', - }, - rangeBehavior: vscode.DecorationRangeBehavior.OpenOpen, - isWholeLine: true, - }) - - constructor(private readonly lineTracker: LineTracker) { - this._disposable = vscode.Disposable.from( - AuthUtil.instance.auth.onDidChangeConnectionState(async (e) => { - if (e.state !== 'authenticating') { - this.hideGenerating() - } - }), - AuthUtil.instance.secondaryAuth.onDidChangeActiveConnection(async () => { - this.hideGenerating() - }) - ) - } - - dispose() { - this._disposable.dispose() - } - - readonly refreshDebounced = cancellableDebounce(async () => { - await this._refresh(true) - }, 1000) - - async showGenerating(triggerType: vscode.InlineCompletionTriggerKind) { - if (triggerType === vscode.InlineCompletionTriggerKind.Invoke) { - // if user triggers on demand, immediately update the UI and cancel the previous debounced update if there is one - this.refreshDebounced.cancel() - await this._refresh(true) - } else { - await this.refreshDebounced.promise() - } - } - - async _refresh(shouldDisplay: boolean) { - const editor = vscode.window.activeTextEditor - if (!editor) { - return - } - - const selections = this.lineTracker.selections - if (!editor || !selections || !editorUtilities.isTextEditor(editor)) { - this.hideGenerating() - return - } - - if (!AuthUtil.instance.isConnectionValid()) { - this.hideGenerating() - return - } - - await this.updateDecorations(editor, selections, shouldDisplay) - } - - hideGenerating() { - vscode.window.activeTextEditor?.setDecorations(this.cwLineHintDecoration, []) - } - - async updateDecorations(editor: vscode.TextEditor, lines: LineSelection[], shouldDisplay: boolean) { - const range = editor.document.validateRange( - new vscode.Range(lines[0].active, lines[0].active, lines[0].active, lines[0].active) - ) - - if (shouldDisplay) { - editor.setDecorations(this.cwLineHintDecoration, [range]) - } else { - editor.setDecorations(this.cwLineHintDecoration, []) - } - } -} diff --git a/packages/amazonq/src/app/inline/recommendationService.ts b/packages/amazonq/src/app/inline/recommendationService.ts index eafdf39a3b2..3f95e73e20c 100644 --- a/packages/amazonq/src/app/inline/recommendationService.ts +++ b/packages/amazonq/src/app/inline/recommendationService.ts @@ -11,7 +11,6 @@ import { import { CancellationToken, InlineCompletionContext, Position, TextDocument } from 'vscode' import { LanguageClient } from 'vscode-languageclient' import { SessionManager } from './sessionManager' -import { InlineGeneratingMessage } from './inlineGeneratingMessage' import { AuthUtil, CodeWhispererStatusBarManager } from 'aws-core-vscode/codewhisperer' import { TelemetryHelper } from './telemetryHelper' import { ICursorUpdateRecorder } from './cursorUpdateManager' @@ -25,7 +24,6 @@ export interface GetAllRecommendationsOptions { export class RecommendationService { constructor( private readonly sessionManager: SessionManager, - private readonly inlineGeneratingMessage: InlineGeneratingMessage, private cursorUpdateRecorder?: ICursorUpdateRecorder ) {} /** @@ -65,7 +63,6 @@ export class RecommendationService { try { // Show UI indicators only if UI is enabled if (options.showUi) { - await this.inlineGeneratingMessage.showGenerating(context.triggerKind) await statusBar.setLoading() } @@ -149,7 +146,6 @@ export class RecommendationService { } finally { // Remove all UI indicators if UI is enabled if (options.showUi) { - this.inlineGeneratingMessage.hideGenerating() void statusBar.refreshStatusBar() // effectively "stop loading" } } diff --git a/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts b/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts index fbc28feefbb..f41bc6631a0 100644 --- a/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts +++ b/packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts @@ -26,7 +26,6 @@ import { ReferenceLogViewProvider, vsCodeState, } from 'aws-core-vscode/codewhisperer' -import { InlineGeneratingMessage } from '../../../../../src/app/inline/inlineGeneratingMessage' import { LineTracker } from '../../../../../src/app/inline/stateTracker/lineTracker' import { InlineTutorialAnnotation } from '../../../../../src/app/inline/tutorials/inlineTutorialAnnotation' @@ -247,9 +246,8 @@ describe('InlineCompletionManager', () => { beforeEach(() => { const lineTracker = new LineTracker() - const activeStateController = new InlineGeneratingMessage(lineTracker) inlineTutorialAnnotation = new InlineTutorialAnnotation(lineTracker, mockSessionManager) - recommendationService = new RecommendationService(mockSessionManager, activeStateController) + recommendationService = new RecommendationService(mockSessionManager) vsCodeState.isRecommendationsActive = false mockSessionManager = { getActiveSession: getActiveSessionStub, diff --git a/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts b/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts index c79c615e520..744fcc63c53 100644 --- a/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts +++ b/packages/amazonq/test/unit/amazonq/apps/inline/recommendationService.test.ts @@ -10,8 +10,6 @@ import assert from 'assert' import { RecommendationService } from '../../../../../src/app/inline/recommendationService' import { SessionManager } from '../../../../../src/app/inline/sessionManager' import { createMockDocument } from 'aws-core-vscode/test' -import { LineTracker } from '../../../../../src/app/inline/stateTracker/lineTracker' -import { InlineGeneratingMessage } from '../../../../../src/app/inline/inlineGeneratingMessage' // Import CursorUpdateManager directly instead of the interface import { CursorUpdateManager } from '../../../../../src/app/inline/cursorUpdateManager' import { CodeWhispererStatusBarManager } from 'aws-core-vscode/codewhisperer' @@ -22,8 +20,6 @@ describe('RecommendationService', () => { let sendRequestStub: sinon.SinonStub let sandbox: sinon.SinonSandbox let sessionManager: SessionManager - let lineTracker: LineTracker - let activeStateController: InlineGeneratingMessage let service: RecommendationService let cursorUpdateManager: CursorUpdateManager let statusBarStub: any @@ -69,8 +65,6 @@ describe('RecommendationService', () => { } as unknown as LanguageClient sessionManager = new SessionManager() - lineTracker = new LineTracker() - activeStateController = new InlineGeneratingMessage(lineTracker) // Create cursor update manager mock cursorUpdateManager = { @@ -94,7 +88,7 @@ describe('RecommendationService', () => { sandbox.stub(CodeWhispererStatusBarManager, 'instance').get(() => statusBarStub) // Create the service without cursor update recorder initially - service = new RecommendationService(sessionManager, activeStateController) + service = new RecommendationService(sessionManager) }) afterEach(() => { @@ -104,11 +98,7 @@ describe('RecommendationService', () => { describe('constructor', () => { it('should initialize with optional cursorUpdateRecorder', () => { - const serviceWithRecorder = new RecommendationService( - sessionManager, - activeStateController, - cursorUpdateManager - ) + const serviceWithRecorder = new RecommendationService(sessionManager, cursorUpdateManager) // Verify the service was created with the recorder assert.strictEqual(serviceWithRecorder['cursorUpdateRecorder'], cursorUpdateManager) @@ -232,26 +222,7 @@ describe('RecommendationService', () => { sinon.assert.calledOnce(cursorUpdateManager.recordCompletionRequest as sinon.SinonStub) }) - // Helper function to setup UI test - function setupUITest() { - const mockFirstResult = { - sessionId: 'test-session', - items: [mockInlineCompletionItemOne], - partialResultToken: undefined, - } - - sendRequestStub.resolves(mockFirstResult) - - // Spy on the UI methods - const showGeneratingStub = sandbox.stub(activeStateController, 'showGenerating').resolves() - const hideGeneratingStub = sandbox.stub(activeStateController, 'hideGenerating') - - return { showGeneratingStub, hideGeneratingStub } - } - it('should not show UI indicators when showUi option is false', async () => { - const { showGeneratingStub, hideGeneratingStub } = setupUITest() - // Call with showUi: false option await service.getAllRecommendations( languageClient, @@ -267,15 +238,11 @@ describe('RecommendationService', () => { ) // Verify UI methods were not called - sinon.assert.notCalled(showGeneratingStub) - sinon.assert.notCalled(hideGeneratingStub) sinon.assert.notCalled(statusBarStub.setLoading) sinon.assert.notCalled(statusBarStub.refreshStatusBar) }) it('should show UI indicators when showUi option is true (default)', async () => { - const { showGeneratingStub, hideGeneratingStub } = setupUITest() - // Call with default options (showUi: true) await service.getAllRecommendations( languageClient, @@ -287,8 +254,6 @@ describe('RecommendationService', () => { ) // Verify UI methods were called - sinon.assert.calledOnce(showGeneratingStub) - sinon.assert.calledOnce(hideGeneratingStub) sinon.assert.calledOnce(statusBarStub.setLoading) sinon.assert.calledOnce(statusBarStub.refreshStatusBar) }) @@ -304,10 +269,6 @@ describe('RecommendationService', () => { // Set up UI options const options = { showUi: true } - // Stub the UI methods to avoid errors - // const showGeneratingStub = sandbox.stub(activeStateController, 'showGenerating').resolves() - const hideGeneratingStub = sandbox.stub(activeStateController, 'hideGenerating') - // Temporarily replace console.error with a no-op function to prevent test failure const originalConsoleError = console.error console.error = () => {} @@ -328,7 +289,6 @@ describe('RecommendationService', () => { assert.deepStrictEqual(result, []) // Verify the UI indicators were hidden even when an error occurs - sinon.assert.calledOnce(hideGeneratingStub) sinon.assert.calledOnce(statusBarStub.refreshStatusBar) } finally { // Restore the original console.error function