Skip to content

Commit 77f53aa

Browse files
committed
Testing opening the read files and code refactoring
1 parent 5825d42 commit 77f53aa

File tree

5 files changed

+116
-77
lines changed

5 files changed

+116
-77
lines changed

packages/core/src/amazonq/webview/ui/apps/cwChatConnector.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,6 @@ export class Connector extends BaseConnector {
357357
if (
358358
!this.onChatAnswerUpdated ||
359359
![
360-
'accept-code-diff',
361360
'reject-code-diff',
362361
'run-shell-command',
363362
'reject-shell-command',
@@ -379,17 +378,6 @@ export class Connector extends BaseConnector {
379378
header: currentChatItem?.header ? { ...currentChatItem.header } : {},
380379
}
381380
switch (action.id) {
382-
case 'accept-code-diff':
383-
if (answer.header) {
384-
answer.header.status = {
385-
icon: 'ok' as MynahIconsType,
386-
text: 'Accepted',
387-
status: 'success',
388-
}
389-
answer.header.buttons = []
390-
answer.body = ' '
391-
}
392-
break
393381
case 'reject-code-diff':
394382
if (answer.header) {
395383
answer.header.status = {

packages/core/src/codewhispererChat/clients/chat/v0/chat.ts

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,27 @@ export type ToolUseWithError = {
2424
error: Error | undefined
2525
}
2626

27+
type OperationType = 'read' | 'write' | 'listDir'
28+
29+
interface FileOperation {
30+
type: OperationType
31+
filePaths: DocumentReference[]
32+
}
33+
2734
export class ChatSession {
2835
private sessionId: string
2936
/**
30-
* _readFiles = list of files read from the project to gather context before generating response.
3137
* _showDiffOnFileWrite = Controls whether to show diff view (true) or file context view (false) to the user
3238
* _context = Additional context to be passed to the LLM for generating the response
3339
* _messageIdToUpdate = messageId of a chat message to be updated, used for reducing consecutive tool messages
3440
*/
35-
private _readFiles: DocumentReference[] = []
36-
private _readFolders: DocumentReference[] = []
3741
private _toolUseWithError: ToolUseWithError | undefined
3842
private _showDiffOnFileWrite: boolean = false
3943
private _context: PromptMessage['context']
4044
private _pairProgrammingModeOn: boolean = true
4145
private _fsWriteBackups: Map<string, FsWriteBackup> = new Map()
4246
private _agenticLoopInProgress: boolean = false
47+
private _messageOperations: Map<string, FileOperation> = new Map()
4348

4449
/**
4550
* True if messages from local history have been sent to session.
@@ -144,30 +149,12 @@ export class ChatSession {
144149
public setSessionID(id: string) {
145150
this.sessionId = id
146151
}
147-
public get readFiles(): DocumentReference[] {
148-
return this._readFiles
149-
}
150-
public get readFolders(): DocumentReference[] {
151-
return this._readFolders
152-
}
153152
public get showDiffOnFileWrite(): boolean {
154153
return this._showDiffOnFileWrite
155154
}
156155
public setShowDiffOnFileWrite(value: boolean) {
157156
this._showDiffOnFileWrite = value
158157
}
159-
public addToReadFiles(filePath: DocumentReference) {
160-
this._readFiles.push(filePath)
161-
}
162-
public clearListOfReadFiles() {
163-
this._readFiles = []
164-
}
165-
public setReadFolders(folder: DocumentReference) {
166-
this._readFolders.push(folder)
167-
}
168-
public clearListOfReadFolders() {
169-
this._readFolders = []
170-
}
171158
async chatIam(chatRequest: SendMessageRequest): Promise<SendMessageCommandOutput> {
172159
const client = await createQDeveloperStreamingClient()
173160

@@ -196,4 +183,56 @@ export class ChatSession {
196183

197184
return response
198185
}
186+
187+
/**
188+
* Adds a file operation for a specific message
189+
* @param messageId The ID of the message
190+
* @param type The type of operation ('read' or 'write')
191+
* @param filePaths Array of DocumentReference involved in the operation
192+
*/
193+
public addMessageOperation(messageId: string, type: OperationType, filePaths: DocumentReference[]) {
194+
this._messageOperations.set(messageId, { type, filePaths })
195+
}
196+
197+
/**
198+
* Gets the file operation details for a specific message
199+
* @param messageId The ID of the message
200+
* @returns The file operation details or undefined if not found
201+
*/
202+
public getMessageOperation(messageId: string): FileOperation | undefined {
203+
return this._messageOperations.get(messageId)
204+
}
205+
206+
/**
207+
* Gets all file paths along with line ranges associated with a message
208+
* @param messageId The ID of the message
209+
* @returns Array of DocumentReference or empty array if message ID not found
210+
*/
211+
public getFilePathsByMessageId(messageId: string): DocumentReference[] {
212+
return this._messageOperations.get(messageId)?.filePaths || []
213+
}
214+
215+
/**
216+
* Gets the operation type for a specific message
217+
* @param messageId The ID of the message
218+
* @returns The operation type or undefined if message ID not found
219+
*/
220+
public getOperationTypeByMessageId(messageId: string): OperationType | undefined {
221+
return this._messageOperations.get(messageId)?.type
222+
}
223+
224+
/**
225+
* Clears the operation for a specific message
226+
* @param messageId The ID of the message
227+
*/
228+
public clearMessageOperation(messageId: string) {
229+
this._messageOperations.delete(messageId)
230+
}
231+
232+
/**
233+
* Clears all message operations
234+
*/
235+
public clearAllMessageOperations() {
236+
this._messageOperations.clear()
237+
}
199238
}

packages/core/src/codewhispererChat/controllers/chat/controller.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,7 @@ export class ChatController {
854854
}
855855

856856
private async closeDiffView(message: CustomFormActionMessage) {
857-
// Close the diff view if User rejected or accepted the generated code changes.
857+
// Close the diff view if User rejected the generated code changes or asked a different question.
858858
if (vscode.window.tabGroups.activeTabGroup.activeTab?.label.includes(amazonQTabSuffix)) {
859859
await vscode.commands.executeCommand('workbench.action.closeActiveEditor')
860860
}
@@ -919,9 +919,6 @@ export class ChatController {
919919
)
920920
}
921921
break
922-
case 'accept-code-diff':
923-
await this.closeDiffView(message)
924-
break
925922
case 'reject-code-diff':
926923
await this.restoreBackup(message)
927924
await this.closeDiffView(message)
@@ -1006,7 +1003,26 @@ export class ChatController {
10061003
}
10071004

10081005
private async processFileClickMessage(message: FileClick) {
1006+
/**
1007+
* This function is used for 3 useCases
1008+
* 1. Read files/folders for Agentic chat
1009+
* 2. Read files in workspace context: Project falcon
1010+
* 3. Open code diff for generated files in Agentic chat.
1011+
*/
10091012
const session = this.sessionStorage.getSession(message.tabID)
1013+
1014+
if (session.getMessageOperation(message.messageId)?.type === 'read') {
1015+
try {
1016+
await vscode.commands.executeCommand('vscode.open', vscode.Uri.file(message.filePath))
1017+
} catch {
1018+
void vscode.window.showInformationMessage(
1019+
`Sorry, Amazon Q failed to open the file: ${path.basename(message.filePath)}`
1020+
)
1021+
}
1022+
} else if (session.getMessageOperation(message.messageId)?.type === 'listDir') {
1023+
void vscode.window.showInformationMessage(`Analyzed the directory: ${message.filePath}`)
1024+
}
1025+
10101026
// Check if user clicked on filePath in the contextList or in the fileListTree and perform the functionality accordingly.
10111027
if (session.showDiffOnFileWrite) {
10121028
const toolUseId = message.messageId
@@ -1030,7 +1046,9 @@ export class ChatController {
10301046
)
10311047
} catch (error) {
10321048
getLogger().error(`Unexpected error in diff view generation: ${error}`)
1033-
void vscode.window.showErrorMessage(`Failed to open diff view.`)
1049+
void vscode.window.showErrorMessage(
1050+
`Sorry, Amazon Q failed to open the diff view for ${path.basename(message.filePath)}`
1051+
)
10341052
}
10351053
} else {
10361054
const lineRanges = session.contexts.get(message.filePath)
@@ -1303,8 +1321,6 @@ export class ChatController {
13031321
// Create a fresh token for this new conversation
13041322
session.createNewTokenSource()
13051323
session.setAgenticLoopInProgress(true)
1306-
session.clearListOfReadFiles()
1307-
session.clearListOfReadFolders()
13081324
session.setShowDiffOnFileWrite(false)
13091325
session.setMessageIdToUpdate(undefined)
13101326
session.setMessageIdToUpdateListDirectory(undefined)

packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ import {
5656
} from '@aws/mynah-ui'
5757
import { Database } from '../../../../shared/db/chatDb/chatDb'
5858
import { TabType } from '../../../../amazonq/webview/ui/storages/tabsStorage'
59-
import { ToolType, ToolUtils } from '../../../tools/toolUtils'
59+
import { Tool, ToolType, ToolUtils } from '../../../tools/toolUtils'
6060
import { ChatStream } from '../../../tools/chatStream'
6161
import path from 'path'
6262
import { CommandValidation, ExecuteBashParams } from '../../../tools/executeBash'
@@ -193,6 +193,29 @@ export class Messenger {
193193
return codeBlocks.length
194194
}
195195

196+
public handleFileReadOrListOperation = (session: ChatSession, toolUse: ToolUse, tool: Tool) => {
197+
const messageIdToUpdate =
198+
tool.type === ToolType.FsRead ? session.messageIdToUpdate : session.messageIdToUpdateListDirectory
199+
const messageId = messageIdToUpdate ?? toolUse?.toolUseId ?? ''
200+
const operationType = tool.type === ToolType.FsRead ? 'read' : 'listDir'
201+
const input = toolUse.input as unknown as FsReadParams | ListDirectoryParams
202+
const existingPaths = session.getFilePathsByMessageId(messageId)
203+
204+
// Check if path already exists in the list
205+
const isPathAlreadyProcessed = existingPaths.some((path) => path.relativeFilePath === input.path)
206+
207+
if (!isPathAlreadyProcessed) {
208+
session.addMessageOperation(messageId, operationType, [
209+
...existingPaths,
210+
{
211+
relativeFilePath: input.path,
212+
lineRanges: [{ first: -1, second: -1 }],
213+
},
214+
])
215+
}
216+
return messageIdToUpdate
217+
}
218+
196219
public async sendAIResponse(
197220
response: MessengerResponseType,
198221
session: ChatSession,
@@ -335,32 +358,8 @@ export class Messenger {
335358
session.setShowDiffOnFileWrite(true)
336359
changeList = await tool.tool.getDiffChanges()
337360
}
338-
if (tool.type === ToolType.FsRead) {
339-
messageIdToUpdate = session.messageIdToUpdate
340-
const input = toolUse.input as unknown as FsReadParams
341-
// Check if this file path is already in the readFiles list
342-
const isFileAlreadyRead = session.readFiles.some(
343-
(file) => file.relativeFilePath === input.path
344-
)
345-
if (!isFileAlreadyRead) {
346-
session.addToReadFiles({
347-
relativeFilePath: input?.path,
348-
lineRanges: [{ first: -1, second: -1 }],
349-
})
350-
}
351-
} else if (tool.type === ToolType.ListDirectory) {
352-
messageIdToUpdate = session.messageIdToUpdateListDirectory
353-
const input = toolUse.input as unknown as ListDirectoryParams
354-
// Check if this folder is already in the readFolders list
355-
const isFolderAlreadyRead = session.readFolders.some(
356-
(folder) => folder.relativeFilePath === input.path
357-
)
358-
if (!isFolderAlreadyRead) {
359-
session.setReadFolders({
360-
relativeFilePath: input?.path,
361-
lineRanges: [{ first: -1, second: -1 }],
362-
})
363-
}
361+
if (isReadOrList) {
362+
messageIdToUpdate = this.handleFileReadOrListOperation(session, toolUse, tool)
364363
}
365364
const validation = ToolUtils.requiresAcceptance(tool)
366365
const chatStream = new ChatStream(
@@ -381,14 +380,11 @@ export class Messenger {
381380
chatStream,
382381
chatStream.validation.requiresAcceptance
383382
)
384-
if (session.messageIdToUpdate === undefined && tool.type === ToolType.FsRead) {
383+
if (!session.messageIdToUpdate && tool.type === ToolType.FsRead) {
385384
// Store the first messageId in a chain of tool uses
386385
session.setMessageIdToUpdate(toolUse.toolUseId)
387386
}
388-
if (
389-
session.messageIdToUpdateListDirectory === undefined &&
390-
tool.type === ToolType.ListDirectory
391-
) {
387+
if (!session.messageIdToUpdateListDirectory && tool.type === ToolType.ListDirectory) {
392388
session.setMessageIdToUpdateListDirectory(toolUse.toolUseId)
393389
}
394390
getLogger().debug(
@@ -706,10 +702,10 @@ export class Messenger {
706702
triggerID: string,
707703
messageIdToUpdate?: string
708704
) {
709-
const contextList = toolUse.name === ToolType.ListDirectory ? session.readFolders : session.readFiles
705+
const messageID = messageIdToUpdate ?? toolUse?.toolUseId ?? ''
706+
const contextList = messageIdToUpdate || toolUse?.toolUseId ? session.getFilePathsByMessageId(messageID) : []
710707
const isFileRead = toolUse.name === ToolType.FsRead
711-
const items = isFileRead ? session.readFiles : session.readFolders
712-
const itemCount = items.length
708+
const itemCount = contextList.length
713709

714710
const title =
715711
itemCount < 1
@@ -727,7 +723,7 @@ export class Messenger {
727723
followUpsHeader: undefined,
728724
relatedSuggestions: undefined,
729725
triggerID,
730-
messageID: messageIdToUpdate ?? toolUse?.toolUseId ?? '',
726+
messageID,
731727
userIntent: undefined,
732728
codeBlockLanguage: undefined,
733729
contextList,

packages/core/src/codewhispererChat/tools/chatStream.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class ChatStream extends Writable {
3737
) {
3838
super()
3939
this.logger.debug(
40-
`ChatStream created for tabID: ${tabID}, triggerID: ${triggerID}, readFiles: ${session.readFiles}, emitEvent to mynahUI: ${emitEvent}`
40+
`ChatStream created for tabID: ${tabID}, triggerID: ${triggerID}, emitEvent to mynahUI: ${emitEvent}`
4141
)
4242
if (!emitEvent) {
4343
return

0 commit comments

Comments
 (0)