Skip to content

Commit 1184ee0

Browse files
Merge master into feature/code-diff
2 parents 56c40fc + d6397ad commit 1184ee0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+8156
-39
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
"webpack-merge": "^5.10.0"
7575
},
7676
"dependencies": {
77-
"@aws/language-server-runtimes": "^0.2.125",
77+
"@aws/language-server-runtimes": "^0.2.128",
7878
"@types/node": "^22.7.5",
7979
"jaro-winkler": "^0.2.8",
8080
"vscode-nls": "^5.2.0",

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

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,73 @@
55

66
import vscode from 'vscode'
77
import {
8+
acceptSuggestion,
89
AuthUtil,
10+
CodeSuggestionsState,
11+
CodeWhispererCodeCoverageTracker,
912
CodeWhispererConstants,
13+
CodeWhispererSettings,
14+
ConfigurationEntry,
15+
DefaultCodeWhispererClient,
16+
invokeRecommendation,
1017
isInlineCompletionEnabled,
18+
KeyStrokeHandler,
19+
RecommendationHandler,
1120
runtimeLanguageContext,
21+
TelemetryHelper,
1222
UserWrittenCodeTracker,
1323
vsCodeState,
1424
} from 'aws-core-vscode/codewhisperer'
15-
import { globals, sleep } from 'aws-core-vscode/shared'
25+
import { Commands, getLogger, globals, sleep } from 'aws-core-vscode/shared'
26+
import { LanguageClient } from 'vscode-languageclient'
1627

17-
export async function activate() {
18-
if (isInlineCompletionEnabled()) {
19-
// Debugging purpose: only initialize NextEditPredictionPanel when development
20-
// NextEditPredictionPanel.getInstance()
28+
export async function activate(languageClient: LanguageClient) {
29+
const codewhispererSettings = CodeWhispererSettings.instance
30+
const client = new DefaultCodeWhispererClient()
2131

32+
if (isInlineCompletionEnabled()) {
2233
await setSubscriptionsforInlineCompletion()
2334
await AuthUtil.instance.setVscodeContextProps()
35+
RecommendationHandler.instance.setLanguageClient(languageClient)
36+
}
37+
38+
function getAutoTriggerStatus(): boolean {
39+
return CodeSuggestionsState.instance.isSuggestionsEnabled()
40+
}
41+
42+
async function getConfigEntry(): Promise<ConfigurationEntry> {
43+
const isShowMethodsEnabled: boolean =
44+
vscode.workspace.getConfiguration('editor').get('suggest.showMethods') || false
45+
const isAutomatedTriggerEnabled: boolean = getAutoTriggerStatus()
46+
const isManualTriggerEnabled: boolean = true
47+
const isSuggestionsWithCodeReferencesEnabled = codewhispererSettings.isSuggestionsWithCodeReferencesEnabled()
48+
49+
// TODO:remove isManualTriggerEnabled
50+
return {
51+
isShowMethodsEnabled,
52+
isManualTriggerEnabled,
53+
isAutomatedTriggerEnabled,
54+
isSuggestionsWithCodeReferencesEnabled,
55+
}
2456
}
2557

2658
async function setSubscriptionsforInlineCompletion() {
59+
RecommendationHandler.instance.subscribeSuggestionCommands()
60+
2761
/**
2862
* Automated trigger
2963
*/
3064
globals.context.subscriptions.push(
65+
acceptSuggestion.register(globals.context),
66+
vscode.window.onDidChangeActiveTextEditor(async (editor) => {
67+
await RecommendationHandler.instance.onEditorChange()
68+
}),
69+
vscode.window.onDidChangeWindowState(async (e) => {
70+
await RecommendationHandler.instance.onFocusChange()
71+
}),
72+
vscode.window.onDidChangeTextEditorSelection(async (e) => {
73+
await RecommendationHandler.instance.onCursorChange(e)
74+
}),
3175
vscode.workspace.onDidChangeTextDocument(async (e) => {
3276
const editor = vscode.window.activeTextEditor
3377
if (!editor) {
@@ -40,6 +84,7 @@ export async function activate() {
4084
return
4185
}
4286

87+
CodeWhispererCodeCoverageTracker.getTracker(e.document.languageId)?.countTotalTokens(e)
4388
UserWrittenCodeTracker.instance.onTextDocumentChange(e)
4489
/**
4590
* Handle this keystroke event only when
@@ -51,6 +96,11 @@ export async function activate() {
5196
return
5297
}
5398

99+
if (vsCodeState.lastUserModificationTime) {
100+
TelemetryHelper.instance.setTimeSinceLastModification(
101+
performance.now() - vsCodeState.lastUserModificationTime
102+
)
103+
}
54104
vsCodeState.lastUserModificationTime = performance.now()
55105
/**
56106
* Important: Doing this sleep(10) is to make sure
@@ -59,6 +109,19 @@ export async function activate() {
59109
* Then this event can be processed by our code.
60110
*/
61111
await sleep(CodeWhispererConstants.vsCodeCursorUpdateDelay)
112+
if (!RecommendationHandler.instance.isSuggestionVisible()) {
113+
await KeyStrokeHandler.instance.processKeyStroke(e, editor, client, await getConfigEntry())
114+
}
115+
}),
116+
// manual trigger
117+
Commands.register({ id: 'aws.amazonq.invokeInlineCompletion', autoconnect: true }, async () => {
118+
invokeRecommendation(
119+
vscode.window.activeTextEditor as vscode.TextEditor,
120+
client,
121+
await getConfigEntry()
122+
).catch((e: Error) => {
123+
getLogger().error('invokeRecommendation failed: %s', (e as Error).message)
124+
})
62125
})
63126
)
64127
}

packages/amazonq/src/lsp/client.ts

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
import {
2222
AuthUtil,
2323
CodeWhispererSettings,
24+
FeatureConfigProvider,
2425
getSelectedCustomization,
2526
TelemetryHelper,
2627
vsCodeState,
@@ -45,6 +46,7 @@ import {
4546
} from 'aws-core-vscode/shared'
4647
import { processUtils } from 'aws-core-vscode/shared'
4748
import { activate } from './chat/activation'
49+
import { activate as activateInline } from '../app/inline/activation'
4850
import { AmazonQResourcePaths } from './lspInstaller'
4951
import { ConfigSection, isValidConfigSection, pushConfigUpdate, toAmazonQLSPLogLevel } from './config'
5052
import { activate as activateInlineChat } from '../inlineChat/activation'
@@ -339,8 +341,42 @@ async function onLanguageServerReady(
339341
// tutorial for inline chat
340342
const inlineChatTutorialAnnotation = new InlineChatTutorialAnnotation(inlineTutorialAnnotation)
341343

342-
const inlineManager = new InlineCompletionManager(client, sessionManager, lineTracker, inlineTutorialAnnotation)
343-
inlineManager.registerInlineCompletion()
344+
const enableInlineRollback = FeatureConfigProvider.instance.getPreFlareRollbackGroup() === 'treatment'
345+
if (enableInlineRollback) {
346+
// use VSC inline
347+
getLogger().info('Entering preflare logic')
348+
await activateInline(client)
349+
} else {
350+
// use language server for inline completion
351+
getLogger().info('Entering postflare logic')
352+
const inlineManager = new InlineCompletionManager(client, sessionManager, lineTracker, inlineTutorialAnnotation)
353+
inlineManager.registerInlineCompletion()
354+
toDispose.push(
355+
inlineManager,
356+
Commands.register('aws.amazonq.showPrev', async () => {
357+
await sessionManager.maybeRefreshSessionUx()
358+
await vscode.commands.executeCommand('editor.action.inlineSuggest.showPrevious')
359+
sessionManager.onPrevSuggestion()
360+
}),
361+
Commands.register('aws.amazonq.showNext', async () => {
362+
await sessionManager.maybeRefreshSessionUx()
363+
await vscode.commands.executeCommand('editor.action.inlineSuggest.showNext')
364+
sessionManager.onNextSuggestion()
365+
}),
366+
// this is a workaround since handleDidShowCompletionItem is not public API
367+
Commands.register('aws.amazonq.checkInlineSuggestionVisibility', async () => {
368+
sessionManager.checkInlineSuggestionVisibility()
369+
}),
370+
Commands.register({ id: 'aws.amazonq.invokeInlineCompletion', autoconnect: true }, async () => {
371+
vsCodeState.lastManualTriggerTime = performance.now()
372+
await vscode.commands.executeCommand('editor.action.inlineSuggest.trigger')
373+
}),
374+
vscode.workspace.onDidCloseTextDocument(async () => {
375+
await vscode.commands.executeCommand('aws.amazonq.rejectCodeSuggestion')
376+
})
377+
)
378+
}
379+
344380
activateInlineChat(extensionContext, client, encryptionKey, inlineChatTutorialAnnotation)
345381

346382
if (Experiments.instance.get('amazonqChatLSP', true)) {
@@ -355,25 +391,6 @@ async function onLanguageServerReady(
355391
await initializeLanguageServerConfiguration(client, 'startup')
356392

357393
toDispose.push(
358-
inlineManager,
359-
Commands.register('aws.amazonq.showPrev', async () => {
360-
await sessionManager.maybeRefreshSessionUx()
361-
await vscode.commands.executeCommand('editor.action.inlineSuggest.showPrevious')
362-
sessionManager.onPrevSuggestion()
363-
}),
364-
Commands.register('aws.amazonq.showNext', async () => {
365-
await sessionManager.maybeRefreshSessionUx()
366-
await vscode.commands.executeCommand('editor.action.inlineSuggest.showNext')
367-
sessionManager.onNextSuggestion()
368-
}),
369-
// this is a workaround since handleDidShowCompletionItem is not public API
370-
Commands.register('aws.amazonq.checkInlineSuggestionVisibility', async () => {
371-
sessionManager.checkInlineSuggestionVisibility()
372-
}),
373-
Commands.register({ id: 'aws.amazonq.invokeInlineCompletion', autoconnect: true }, async () => {
374-
vsCodeState.lastManualTriggerTime = performance.now()
375-
await vscode.commands.executeCommand('editor.action.inlineSuggest.trigger')
376-
}),
377394
Commands.register('aws.amazonq.refreshAnnotation', async (forceProceed: boolean) => {
378395
telemetry.record({
379396
traceId: TelemetryHelper.instance.traceId,
@@ -399,9 +416,6 @@ async function onLanguageServerReady(
399416
getLogger().debug(`codewhisperer: user dismiss tutorial.`)
400417
}
401418
}),
402-
vscode.workspace.onDidCloseTextDocument(async () => {
403-
await vscode.commands.executeCommand('aws.amazonq.rejectCodeSuggestion')
404-
}),
405419
AuthUtil.instance.auth.onDidChangeActiveConnection(async () => {
406420
await auth.refreshConnection()
407421
}),
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import assert from 'assert'
7+
import * as sinon from 'sinon'
8+
import { resetCodeWhispererGlobalVariables, createMockTextEditor } from 'aws-core-vscode/test'
9+
import {
10+
ConfigurationEntry,
11+
invokeRecommendation,
12+
InlineCompletionService,
13+
isInlineCompletionEnabled,
14+
DefaultCodeWhispererClient,
15+
} from 'aws-core-vscode/codewhisperer'
16+
17+
describe('invokeRecommendation', function () {
18+
describe('invokeRecommendation', function () {
19+
let getRecommendationStub: sinon.SinonStub
20+
let mockClient: DefaultCodeWhispererClient
21+
22+
beforeEach(async function () {
23+
await resetCodeWhispererGlobalVariables()
24+
getRecommendationStub = sinon.stub(InlineCompletionService.instance, 'getPaginatedRecommendation')
25+
})
26+
27+
afterEach(function () {
28+
sinon.restore()
29+
})
30+
31+
it('Should call getPaginatedRecommendation with OnDemand as trigger type when inline completion is enabled', async function () {
32+
const mockEditor = createMockTextEditor()
33+
const config: ConfigurationEntry = {
34+
isShowMethodsEnabled: true,
35+
isManualTriggerEnabled: true,
36+
isAutomatedTriggerEnabled: true,
37+
isSuggestionsWithCodeReferencesEnabled: true,
38+
}
39+
await invokeRecommendation(mockEditor, mockClient, config)
40+
assert.strictEqual(getRecommendationStub.called, isInlineCompletionEnabled())
41+
})
42+
})
43+
})
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import assert from 'assert'
7+
import * as vscode from 'vscode'
8+
import * as sinon from 'sinon'
9+
import { onAcceptance, AcceptedSuggestionEntry, session, CodeWhispererTracker } from 'aws-core-vscode/codewhisperer'
10+
import { resetCodeWhispererGlobalVariables, createMockTextEditor } from 'aws-core-vscode/test'
11+
12+
describe('onAcceptance', function () {
13+
describe('onAcceptance', function () {
14+
beforeEach(async function () {
15+
await resetCodeWhispererGlobalVariables()
16+
session.reset()
17+
})
18+
19+
afterEach(function () {
20+
sinon.restore()
21+
session.reset()
22+
})
23+
24+
it('Should enqueue an event object to tracker', async function () {
25+
const mockEditor = createMockTextEditor()
26+
const trackerSpy = sinon.spy(CodeWhispererTracker.prototype, 'enqueue')
27+
const fakeReferences = [
28+
{
29+
message: '',
30+
licenseName: 'MIT',
31+
repository: 'http://github.com/fake',
32+
recommendationContentSpan: {
33+
start: 0,
34+
end: 10,
35+
},
36+
},
37+
]
38+
await onAcceptance({
39+
editor: mockEditor,
40+
range: new vscode.Range(new vscode.Position(1, 0), new vscode.Position(1, 26)),
41+
effectiveRange: new vscode.Range(new vscode.Position(1, 0), new vscode.Position(1, 26)),
42+
acceptIndex: 0,
43+
recommendation: "print('Hello World!')",
44+
requestId: '',
45+
sessionId: '',
46+
triggerType: 'OnDemand',
47+
completionType: 'Line',
48+
language: 'python',
49+
references: fakeReferences,
50+
})
51+
const actualArg = trackerSpy.getCall(0).args[0] as AcceptedSuggestionEntry
52+
assert.ok(trackerSpy.calledOnce)
53+
assert.strictEqual(actualArg.originalString, 'def two_sum(nums, target):')
54+
assert.strictEqual(actualArg.requestId, '')
55+
assert.strictEqual(actualArg.sessionId, '')
56+
assert.strictEqual(actualArg.triggerType, 'OnDemand')
57+
assert.strictEqual(actualArg.completionType, 'Line')
58+
assert.strictEqual(actualArg.language, 'python')
59+
assert.deepStrictEqual(actualArg.startPosition, new vscode.Position(1, 0))
60+
assert.deepStrictEqual(actualArg.endPosition, new vscode.Position(1, 26))
61+
assert.strictEqual(actualArg.index, 0)
62+
})
63+
})
64+
})
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import assert from 'assert'
7+
import * as vscode from 'vscode'
8+
import * as sinon from 'sinon'
9+
import { resetCodeWhispererGlobalVariables, createMockTextEditor } from 'aws-core-vscode/test'
10+
import { onInlineAcceptance, RecommendationHandler, session } from 'aws-core-vscode/codewhisperer'
11+
12+
describe('onInlineAcceptance', function () {
13+
describe('onInlineAcceptance', function () {
14+
beforeEach(async function () {
15+
await resetCodeWhispererGlobalVariables()
16+
session.reset()
17+
})
18+
19+
afterEach(function () {
20+
sinon.restore()
21+
session.reset()
22+
})
23+
24+
it('Should dispose inline completion provider', async function () {
25+
const mockEditor = createMockTextEditor()
26+
const spy = sinon.spy(RecommendationHandler.instance, 'disposeInlineCompletion')
27+
await onInlineAcceptance({
28+
editor: mockEditor,
29+
range: new vscode.Range(new vscode.Position(1, 0), new vscode.Position(1, 21)),
30+
effectiveRange: new vscode.Range(new vscode.Position(1, 0), new vscode.Position(1, 21)),
31+
acceptIndex: 0,
32+
recommendation: "print('Hello World!')",
33+
requestId: '',
34+
sessionId: '',
35+
triggerType: 'OnDemand',
36+
completionType: 'Line',
37+
language: 'python',
38+
references: undefined,
39+
})
40+
assert.ok(spy.calledWith())
41+
})
42+
})
43+
})

0 commit comments

Comments
 (0)