Skip to content

Commit f09edc5

Browse files
Merge master into feature/ui-e2e-tests
2 parents dd9a0d5 + d50d38e commit f09edc5

File tree

6 files changed

+112
-28
lines changed

6 files changed

+112
-28
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Slightly delay rendering inline completion when user is typing"
4+
}

packages/amazonq/src/app/inline/completion.ts

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
5-
import * as vscode from 'vscode'
5+
66
import {
77
CancellationToken,
88
InlineCompletionContext,
@@ -46,9 +46,7 @@ import { Experiments, getLogger, sleep } from 'aws-core-vscode/shared'
4646
import { debounce, messageUtils } from 'aws-core-vscode/utils'
4747
import { showEdits } from './EditRendering/imageRenderer'
4848
import { ICursorUpdateRecorder } from './cursorUpdateManager'
49-
50-
let lastDocumentDeleteEvent: vscode.TextDocumentChangeEvent | undefined = undefined
51-
let lastDocumentDeleteTime = 0
49+
import { DocumentEventListener } from './documentEventListener'
5250

5351
export class InlineCompletionManager implements Disposable {
5452
private disposable: Disposable
@@ -60,7 +58,7 @@ export class InlineCompletionManager implements Disposable {
6058

6159
private inlineTutorialAnnotation: InlineTutorialAnnotation
6260
private readonly logSessionResultMessageName = 'aws/logInlineCompletionSessionResults'
63-
private documentChangeListener: Disposable
61+
private documentEventListener: DocumentEventListener
6462

6563
constructor(
6664
languageClient: LanguageClient,
@@ -74,24 +72,19 @@ export class InlineCompletionManager implements Disposable {
7472
this.lineTracker = lineTracker
7573
this.recommendationService = new RecommendationService(this.sessionManager, cursorUpdateRecorder)
7674
this.inlineTutorialAnnotation = inlineTutorialAnnotation
75+
this.documentEventListener = new DocumentEventListener()
7776
this.inlineCompletionProvider = new AmazonQInlineCompletionItemProvider(
7877
languageClient,
7978
this.recommendationService,
8079
this.sessionManager,
81-
this.inlineTutorialAnnotation
80+
this.inlineTutorialAnnotation,
81+
this.documentEventListener
8282
)
8383

84-
this.documentChangeListener = vscode.workspace.onDidChangeTextDocument((e) => {
85-
if (e.contentChanges.length === 1 && e.contentChanges[0].text === '') {
86-
lastDocumentDeleteEvent = e
87-
lastDocumentDeleteTime = performance.now()
88-
}
89-
})
9084
this.disposable = languages.registerInlineCompletionItemProvider(
9185
CodeWhispererConstants.platformLanguageIds,
9286
this.inlineCompletionProvider
9387
)
94-
9588
this.lineTracker.ready()
9689
}
9790

@@ -104,8 +97,8 @@ export class InlineCompletionManager implements Disposable {
10497
this.disposable.dispose()
10598
this.lineTracker.dispose()
10699
}
107-
if (this.documentChangeListener) {
108-
this.documentChangeListener.dispose()
100+
if (this.documentEventListener) {
101+
this.documentEventListener.dispose()
109102
}
110103
}
111104

@@ -211,7 +204,8 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem
211204
private readonly languageClient: LanguageClient,
212205
private readonly recommendationService: RecommendationService,
213206
private readonly sessionManager: SessionManager,
214-
private readonly inlineTutorialAnnotation: InlineTutorialAnnotation
207+
private readonly inlineTutorialAnnotation: InlineTutorialAnnotation,
208+
private readonly documentEventListener: DocumentEventListener
215209
) {}
216210

217211
private readonly logSessionResultMessageName = 'aws/logInlineCompletionSessionResults'
@@ -251,8 +245,7 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem
251245
await sleep(1)
252246
// prevent user deletion invoking auto trigger
253247
// this is a best effort estimate of deletion
254-
const timeDiff = Math.abs(performance.now() - lastDocumentDeleteTime)
255-
if (timeDiff < 500 && lastDocumentDeleteEvent && lastDocumentDeleteEvent.document.uri === document.uri) {
248+
if (this.documentEventListener.isLastEventDeletion(document.uri.fsPath)) {
256249
getLogger().debug('Skip auto trigger when deleting code')
257250
return []
258251
}
@@ -393,6 +386,20 @@ ${itemLog}
393386
return []
394387
}
395388

389+
// delay the suggestion rendeing if user is actively typing
390+
// see https://github.com/aws/aws-toolkit-vscode/commit/a537602a96f498f372ed61ec9d82cf8577a9d854
391+
for (let i = 0; i < 30; i++) {
392+
const lastDocumentChange = this.documentEventListener.getLastDocumentChangeEvent(document.uri.fsPath)
393+
if (
394+
lastDocumentChange &&
395+
performance.now() - lastDocumentChange.timestamp < CodeWhispererConstants.inlineSuggestionShowDelay
396+
) {
397+
await sleep(CodeWhispererConstants.showRecommendationTimerPollPeriod)
398+
} else {
399+
break
400+
}
401+
}
402+
396403
// the user typed characters from invoking suggestion cursor position to receiving suggestion position
397404
const typeahead = document.getText(new Range(position, editor.selection.active))
398405

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import * as vscode from 'vscode'
6+
7+
export interface DocumentChangeEvent {
8+
event: vscode.TextDocumentChangeEvent
9+
timestamp: number
10+
}
11+
12+
export class DocumentEventListener {
13+
private lastDocumentChangeEventMap: Map<string, DocumentChangeEvent> = new Map()
14+
private documentChangeListener: vscode.Disposable
15+
private _maxDocument = 1000
16+
17+
constructor() {
18+
this.documentChangeListener = vscode.workspace.onDidChangeTextDocument((e) => {
19+
if (e.contentChanges.length > 0) {
20+
if (this.lastDocumentChangeEventMap.size > this._maxDocument) {
21+
this.lastDocumentChangeEventMap.clear()
22+
}
23+
this.lastDocumentChangeEventMap.set(e.document.uri.fsPath, { event: e, timestamp: performance.now() })
24+
}
25+
})
26+
}
27+
28+
public isLastEventDeletion(filepath: string): boolean {
29+
const result = this.lastDocumentChangeEventMap.get(filepath)
30+
if (result) {
31+
const event = result.event
32+
const eventTime = result.timestamp
33+
const isDelete =
34+
(event && event.contentChanges.length === 1 && event.contentChanges[0].text === '') || false
35+
const timeDiff = Math.abs(performance.now() - eventTime)
36+
return timeDiff < 500 && isDelete
37+
}
38+
return false
39+
}
40+
41+
public getLastDocumentChangeEvent(filepath: string): DocumentChangeEvent | undefined {
42+
return this.lastDocumentChangeEventMap.get(filepath)
43+
}
44+
45+
public dispose(): void {
46+
if (this.documentChangeListener) {
47+
this.documentChangeListener.dispose()
48+
}
49+
}
50+
}

packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
} from 'aws-core-vscode/codewhisperer'
2929
import { LineTracker } from '../../../../../src/app/inline/stateTracker/lineTracker'
3030
import { InlineTutorialAnnotation } from '../../../../../src/app/inline/tutorials/inlineTutorialAnnotation'
31+
import { DocumentEventListener } from '../../../../../src/app/inline/documentEventListener'
3132

3233
describe('InlineCompletionManager', () => {
3334
let manager: InlineCompletionManager
@@ -243,11 +244,13 @@ describe('InlineCompletionManager', () => {
243244
let getAllRecommendationsStub: sinon.SinonStub
244245
let recommendationService: RecommendationService
245246
let inlineTutorialAnnotation: InlineTutorialAnnotation
247+
let documentEventListener: DocumentEventListener
246248

247249
beforeEach(() => {
248250
const lineTracker = new LineTracker()
249251
inlineTutorialAnnotation = new InlineTutorialAnnotation(lineTracker, mockSessionManager)
250252
recommendationService = new RecommendationService(mockSessionManager)
253+
documentEventListener = new DocumentEventListener()
251254
vsCodeState.isRecommendationsActive = false
252255
mockSessionManager = {
253256
getActiveSession: getActiveSessionStub,
@@ -271,7 +274,8 @@ describe('InlineCompletionManager', () => {
271274
languageClient,
272275
recommendationService,
273276
mockSessionManager,
274-
inlineTutorialAnnotation
277+
inlineTutorialAnnotation,
278+
documentEventListener
275279
)
276280
const items = await provider.provideInlineCompletionItems(
277281
mockDocument,
@@ -287,7 +291,8 @@ describe('InlineCompletionManager', () => {
287291
languageClient,
288292
recommendationService,
289293
mockSessionManager,
290-
inlineTutorialAnnotation
294+
inlineTutorialAnnotation,
295+
documentEventListener
291296
)
292297
await provider.provideInlineCompletionItems(mockDocument, mockPosition, mockContext, mockToken)
293298
}),
@@ -296,7 +301,8 @@ describe('InlineCompletionManager', () => {
296301
languageClient,
297302
recommendationService,
298303
mockSessionManager,
299-
inlineTutorialAnnotation
304+
inlineTutorialAnnotation,
305+
documentEventListener
300306
)
301307
getActiveRecommendationStub.returns([
302308
{
@@ -326,7 +332,8 @@ describe('InlineCompletionManager', () => {
326332
languageClient,
327333
recommendationService,
328334
mockSessionManager,
329-
inlineTutorialAnnotation
335+
inlineTutorialAnnotation,
336+
documentEventListener
330337
)
331338
const expectedText = `${mockSuggestions[1].insertText}this is my text`
332339
getActiveRecommendationStub.returns([
@@ -352,7 +359,8 @@ describe('InlineCompletionManager', () => {
352359
languageClient,
353360
recommendationService,
354361
mockSessionManager,
355-
inlineTutorialAnnotation
362+
inlineTutorialAnnotation,
363+
documentEventListener
356364
)
357365
getActiveRecommendationStub.returns([])
358366
const messageShown = new Promise((resolve) =>
@@ -385,7 +393,8 @@ describe('InlineCompletionManager', () => {
385393
languageClient,
386394
recommendationService,
387395
mockSessionManager,
388-
inlineTutorialAnnotation
396+
inlineTutorialAnnotation,
397+
documentEventListener
389398
)
390399
const p1 = provider.provideInlineCompletionItems(mockDocument, mockPosition, mockContext, mockToken)
391400
const p2 = provider.provideInlineCompletionItems(mockDocument, mockPosition, mockContext, mockToken)

packages/core/src/auth/activation.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,28 @@ import { LoginManager } from './deprecated/loginManager'
99
import { fromString } from './providers/credentials'
1010
import { initializeCredentialsProviderManager } from './utils'
1111
import { isAmazonQ, isSageMaker } from '../shared/extensionUtilities'
12+
import { getLogger } from '../shared/logger/logger'
13+
import { getErrorMsg } from '../shared/errors'
1214

1315
interface SagemakerCookie {
1416
authMode?: 'Sso' | 'Iam'
1517
}
1618

1719
export async function initialize(loginManager: LoginManager): Promise<void> {
1820
if (isAmazonQ() && isSageMaker()) {
19-
// The command `sagemaker.parseCookies` is registered in VS Code Sagemaker environment.
20-
const result = (await vscode.commands.executeCommand('sagemaker.parseCookies')) as SagemakerCookie
21-
if (result.authMode !== 'Sso') {
22-
initializeCredentialsProviderManager()
21+
try {
22+
// The command `sagemaker.parseCookies` is registered in VS Code Sagemaker environment.
23+
const result = (await vscode.commands.executeCommand('sagemaker.parseCookies')) as SagemakerCookie
24+
if (result.authMode !== 'Sso') {
25+
initializeCredentialsProviderManager()
26+
}
27+
} catch (e) {
28+
const errMsg = getErrorMsg(e as Error)
29+
if (errMsg?.includes("command 'sagemaker.parseCookies' not found")) {
30+
getLogger().warn(`Failed to execute command "sagemaker.parseCookies": ${e}`)
31+
} else {
32+
throw e
33+
}
2334
}
2435
}
2536
Auth.instance.onDidChangeActiveConnection(async (conn) => {

packages/core/src/codewhisperer/models/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ export const identityPoolID = 'us-east-1:70717e99-906f-4add-908c-bd9074a2f5b9'
184184
*/
185185
export const inlineCompletionsDebounceDelay = 200
186186

187+
// add 200ms more delay on top of inline default 30-50ms
188+
export const inlineSuggestionShowDelay = 200
189+
187190
export const referenceLog = 'Code Reference Log'
188191

189192
export const suggestionDetailReferenceText = (licenses: string) =>

0 commit comments

Comments
 (0)