From 0dae638be79612f42081ef4cf8174897720cc7d8 Mon Sep 17 00:00:00 2001 From: Mathew Pareles Date: Wed, 19 Mar 2025 21:58:12 -0700 Subject: [PATCH] add automatic file adding --- .../contrib/void/browser/chatThreadService.ts | 62 +++++++++++++++++++ .../react/src/sidebar-tsx/SidebarChat.tsx | 21 ++++++- .../contrib/void/browser/sidebarActions.ts | 4 +- .../void/common/chatThreadServiceTypes.ts | 2 + 4 files changed, 85 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index 0a0da257..6fec12c8 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -27,6 +27,8 @@ import { ITerminalToolService } from './terminalToolService.js'; import { IMetricsService } from '../common/metricsService.js'; import { shorten } from '../../../../base/common/labels.js'; import { IVoidModelService } from '../common/voidModelService.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; +import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; const findLastIndex = (arr: T[], condition: (t: T) => boolean): number => { for (let i = arr.length - 1; i >= 0; i--) { @@ -208,6 +210,8 @@ class ChatThreadService extends Disposable implements IChatThreadService { @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, @ITerminalToolService private readonly _terminalToolService: ITerminalToolService, @IMetricsService private readonly _metricsService: IMetricsService, + @IEditorService private readonly _editorService: IEditorService, + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, ) { super() this.state = { allThreads: {}, currentThreadId: null as unknown as string } // default state @@ -222,8 +226,66 @@ class ChatThreadService extends Disposable implements IChatThreadService { // always be in a thread this.openNewThread() + + // when the user changes files, automatically add the new file as a stagingSelection + this._register(this._editorService.onDidActiveEditorChange(() => this._addCurrentFileAsStagingSelectionDuringFileChange())); + } + + private _addCurrentFileAsStagingSelectionDuringFileChange() { + + + // add the current file to the thread/message being edited + const model = this._codeEditorService.getActiveCodeEditor()?.getModel() ?? null + if (!model) { return; } + + const newSelection: StagingSelectionItem = { + type: 'File', + fileURI: model.uri, + language: model.getLanguageId(), + selectionStr: null, + range: null, + state: { isOpened: false, wasAddedAsCurrentFile: true } + } + + const focusedMessageIdx = this.getCurrentFocusedMessageIdx(); + + // add the selection + if (focusedMessageIdx === undefined) { // user is in the default thread + + const oldStagingSelections = this.getCurrentThreadState().stagingSelections || []; + + // if the file already exists, do nothing + const alreadyHasFile = oldStagingSelections.some(s => s.type === 'File' && s.fileURI.toString() === newSelection.fileURI.toString()) + if (alreadyHasFile) { return; } + + // add the file + const filteredStagingSelections = oldStagingSelections.filter(s => !s.state?.wasAddedAsCurrentFile); // remove all old selectons that were added during a file change + const newSelections = [...filteredStagingSelections, newSelection]; + + this.setCurrentThreadState({ stagingSelections: newSelections }); + + + } else { // user is editing a message + + // const oldStagingSelections = this.getCurrentMessageState(focusedMessageIdx).stagingSelections || []; + + // // if the file already exists, do nothing + // const alreadyHasFile = oldStagingSelections.some(s => s.type === 'File' && s.fileURI.toString() === newSelection.fileURI.toString()) + // if (alreadyHasFile) { return; } + + // const filteredStagingSelections = oldStagingSelections.filter(s => !s.state?.wasAddedDuringFileChange); // remove all old selectons that were added during a file change + // const newSelections = [...filteredStagingSelections, newSelection]; + // this.setCurrentMessageState(focusedMessageIdx, { stagingSelections: newSelections }); + + + } + + + } + + // !!! this is important for properly restoring URIs from storage // should probably re-use code from void/src/vs/base/common/marshalling.ts instead. but this is simple enough private _convertThreadDataFromStorage(threadsStr: string): ChatThreads { diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index c913f6f6..f878ce07 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -518,7 +518,7 @@ export const SelectedFiles = ( language: (await modelReferenceService.getModelSafe(uri)).model?.getLanguageId() || 'plaintext', selectionStr: null, range: null, - state: { isOpened: false }, + state: { isOpened: false, wasAddedAsCurrentFile: false }, }) } return answer @@ -548,6 +548,7 @@ export const SelectedFiles = ( const isThisSelectionOpened = (!!selection.selectionStr && selection.state.isOpened && type === 'staging') const isThisSelectionAFile = selection.selectionStr === null const isThisSelectionProspective = i > selections.length - 1 + const isThisSelectionAddedAsCurrentFile = selection.state.wasAddedAsCurrentFile const thisKey = `${isThisSelectionProspective}-${i}-${selections.length}` @@ -582,14 +583,21 @@ export const SelectedFiles = ( if (isThisSelectionProspective) { // add prospective selection to selections setSelections([...selections, selection]) } else if (isThisSelectionAFile) { // open files + commandService.executeCommand('vscode.open', selection.fileURI, { preview: true, // preserveFocus: false, }); + + if (isThisSelectionAddedAsCurrentFile) { + // make it so the file is added permanently, not just as the current file + const newSelection: StagingSelectionItem = { ...selection, state: { ...selection.state, wasAddedAsCurrentFile: false } } + setSelections([...selections.slice(0, i), newSelection, ...selections.slice(i + 1)]) + } } else { // show text const selection = selections[i] - const newSelection = { ...selection, state: { isOpened: !selection.state.isOpened } } + const newSelection = { ...selection, state: { ...selection.state, isOpened: !selection.state.isOpened } } const newSelections = [ ...selections.slice(0, i), newSelection, @@ -611,6 +619,15 @@ export const SelectedFiles = ( + (isThisSelectionAFile ? '' : ` (${selection.range.startLineNumber}-${selection.range.endLineNumber})`) } + {/* {isThisSelectionAFile && currentURI?.toString() === selection.fileURI.toString() && + + {`(Current File)`} + + } */} + {isThisSelectionAddedAsCurrentFile && + {`(Current File)`} + } + {type === 'staging' && !isThisSelectionProspective ? // X button