in packages/core/src/amazonqTest/chat/controller/controller.ts [748:906]
private async acceptCode(message: any) {
const session = this.sessionStorage.getSession()
session.acceptedJobId = session.listOfTestGenerationJobId[session.listOfTestGenerationJobId.length - 1]
const filePath = session.generatedFilePath
const absolutePath = path.join(session.projectRootPath, filePath)
const fileExists = await fs.existsFile(absolutePath)
const buildCommand = session.updatedBuildCommands?.join(' ')
const tempFilePath = path.join(this.tempResultDirPath, 'resultArtifacts', filePath)
const updatedContent = await fs.readFileText(tempFilePath)
let acceptedLines = updatedContent.split('\n').length
let acceptedChars = updatedContent.length
if (fileExists) {
const originalContent = await fs.readFileText(absolutePath)
acceptedLines -= originalContent.split('\n').length
acceptedLines = acceptedLines < 0 ? 0 : acceptedLines
acceptedChars -= originalContent.length
acceptedChars = acceptedChars < 0 ? 0 : acceptedChars
UserWrittenCodeTracker.instance.onQStartsMakingEdits()
const document = await vscode.workspace.openTextDocument(absolutePath)
await applyChanges(
document,
new vscode.Range(document.lineAt(0).range.start, document.lineAt(document.lineCount - 1).range.end),
updatedContent
)
UserWrittenCodeTracker.instance.onQFinishesEdits()
} else {
await fs.writeFile(absolutePath, updatedContent)
}
session.charsOfCodeAccepted = acceptedChars
session.linesOfCodeAccepted = acceptedLines
// add accepted references to reference log, if any
const fileName = path.basename(session.generatedFilePath)
const time = new Date().toLocaleString()
// TODO: this is duplicated in basicCommands.ts for scan (codewhisperer). Fix this later.
for (const reference of session.references) {
getLogger().debug('Processing reference: %O', reference)
// Log values for debugging
getLogger().debug('updatedContent: %s', updatedContent)
getLogger().debug(
'start: %d, end: %d',
reference.recommendationContentSpan?.start,
reference.recommendationContentSpan?.end
)
// given a start and end index, figure out which line number they belong to when splitting a string on /n characters
const getLineNumber = (content: string, index: number): number => {
const lines = content.slice(0, index).split('\n')
return lines.length
}
const startLine = getLineNumber(updatedContent, reference.recommendationContentSpan!.start)
const endLine = getLineNumber(updatedContent, reference.recommendationContentSpan!.end)
getLogger().debug('startLine: %d, endLine: %d', startLine, endLine)
const code = updatedContent.slice(
reference.recommendationContentSpan?.start,
reference.recommendationContentSpan?.end
)
getLogger().debug('Extracted code slice: %s', code)
const referenceLog =
`[${time}] Accepted recommendation ` +
referenceLogText(
`<br><code>${code}</code><br>`,
reference.licenseName!,
reference.repository!,
fileName,
startLine === endLine ? `(line at ${startLine})` : `(lines from ${startLine} to ${endLine})`
) +
'<br>'
getLogger().debug('Adding reference log: %s', referenceLog)
ReferenceLogViewProvider.instance.addReferenceLog(referenceLog)
}
// TODO: see if there's a better way to check if active file is a diff
if (vscode.window.tabGroups.activeTabGroup.activeTab?.label.includes(amazonQTabSuffix)) {
await vscode.commands.executeCommand('workbench.action.closeActiveEditor')
}
const document = await vscode.workspace.openTextDocument(absolutePath)
await vscode.window.showTextDocument(document)
// TODO: send the message once again once build is enabled
// this.messenger.sendMessage('Accepted', message.tabID, 'prompt')
telemetry.ui_click.emit({ elementId: 'unitTestGeneration_acceptDiff' })
getLogger().info(
`Generated unit tests are accepted for ${session.fileLanguage ?? 'plaintext'} language with jobId: ${session.listOfTestGenerationJobId[0]}, jobGroupName: ${session.testGenerationJobGroupName}, result: Succeeded`
)
TelemetryHelper.instance.sendTestGenerationToolkitEvent(
session,
true,
true,
'Succeeded',
session.startTestGenerationRequestId,
session.latencyOfTestGeneration,
undefined,
session.isCodeBlockSelected,
session.artifactsUploadDuration,
session.srcPayloadSize,
session.srcZipFileSize,
session.charsOfCodeAccepted,
session.numberOfTestsGenerated,
session.linesOfCodeAccepted,
session.charsOfCodeGenerated,
session.numberOfTestsGenerated,
session.linesOfCodeGenerated,
undefined,
'ACCEPTED'
)
await this.endSession(message, FollowUpTypes.SkipBuildAndFinish)
return
if (session.listOfTestGenerationJobId.length === 1) {
this.startInitialBuild(message)
this.messenger.sendChatInputEnabled(message.tabID, false)
} else if (session.listOfTestGenerationJobId.length < 4) {
const remainingIterations = 4 - session.listOfTestGenerationJobId.length
let userMessage = 'Would you like Amazon Q to build and execute again, and fix errors?'
if (buildCommand) {
userMessage += ` I will be running this build command: \`${buildCommand}\``
}
userMessage += `\nYou have ${remainingIterations} iteration${remainingIterations > 1 ? 's' : ''} left.`
const followUps: FollowUps = {
text: '',
options: [
{
pillText: `Rebuild`,
type: FollowUpTypes.ContinueBuildAndExecute,
status: 'primary',
},
{
pillText: `Skip and finish`,
type: FollowUpTypes.SkipBuildAndFinish,
status: 'primary',
},
],
}
this.messenger.sendBuildProgressMessage({
tabID: message.tabID,
messageType: 'answer',
codeGenerationId: '',
message: userMessage,
canBeVoted: false,
messageId: '',
followUps: followUps,
})
this.messenger.sendChatInputEnabled(message.tabID, false)
} else {
this.sessionStorage.getSession().listOfTestGenerationJobId = []
this.messenger.sendMessage(
'You have gone through both iterations and this unit test generation workflow is complete.',
message.tabID,
'answer'
)
await this.sessionCleanUp()
}
await fs.delete(this.tempResultDirPath, { recursive: true })
}