fix: manually attached files should take precedence over suggested files (#234291)
This commit is contained in:
Родитель
543d860c85
Коммит
b958720bd5
|
@ -22,7 +22,7 @@ import { GroupsOrder, IEditorGroupsService } from '../../../../services/editor/c
|
||||||
import { IEditorService } from '../../../../services/editor/common/editorService.js';
|
import { IEditorService } from '../../../../services/editor/common/editorService.js';
|
||||||
import { ChatAgentLocation } from '../../common/chatAgents.js';
|
import { ChatAgentLocation } from '../../common/chatAgents.js';
|
||||||
import { ChatContextKeys } from '../../common/chatContextKeys.js';
|
import { ChatContextKeys } from '../../common/chatContextKeys.js';
|
||||||
import { applyingChatEditsFailedContextKey, CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, chatEditingResourceContextKey, chatEditingWidgetFileStateContextKey, decidedChatEditingResourceContextKey, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, IChatEditingSession, WorkingSetEntryState } from '../../common/chatEditingService.js';
|
import { applyingChatEditsFailedContextKey, CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, chatEditingResourceContextKey, chatEditingWidgetFileStateContextKey, decidedChatEditingResourceContextKey, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, IChatEditingSession, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../../common/chatEditingService.js';
|
||||||
import { IChatService } from '../../common/chatService.js';
|
import { IChatService } from '../../common/chatService.js';
|
||||||
import { isRequestVM, isResponseVM } from '../../common/chatViewModel.js';
|
import { isRequestVM, isResponseVM } from '../../common/chatViewModel.js';
|
||||||
import { CHAT_CATEGORY } from '../actions/chatActions.js';
|
import { CHAT_CATEGORY } from '../actions/chatActions.js';
|
||||||
|
@ -96,7 +96,7 @@ registerAction2(class RemoveFileFromWorkingSet extends WorkingSetAction {
|
||||||
|
|
||||||
async runWorkingSetAction(accessor: ServicesAccessor, currentEditingSession: IChatEditingSession, chatWidget: IChatWidget, ...uris: URI[]): Promise<void> {
|
async runWorkingSetAction(accessor: ServicesAccessor, currentEditingSession: IChatEditingSession, chatWidget: IChatWidget, ...uris: URI[]): Promise<void> {
|
||||||
// Remove from working set
|
// Remove from working set
|
||||||
currentEditingSession.remove(...uris);
|
currentEditingSession.remove(WorkingSetEntryRemovalReason.User, ...uris);
|
||||||
|
|
||||||
// Remove from chat input part
|
// Remove from chat input part
|
||||||
const resourceSet = new ResourceSet(uris);
|
const resourceSet = new ResourceSet(uris);
|
||||||
|
|
|
@ -30,7 +30,7 @@ import { IEditorService } from '../../../../services/editor/common/editorService
|
||||||
import { MultiDiffEditor } from '../../../multiDiffEditor/browser/multiDiffEditor.js';
|
import { MultiDiffEditor } from '../../../multiDiffEditor/browser/multiDiffEditor.js';
|
||||||
import { MultiDiffEditorInput } from '../../../multiDiffEditor/browser/multiDiffEditorInput.js';
|
import { MultiDiffEditorInput } from '../../../multiDiffEditor/browser/multiDiffEditorInput.js';
|
||||||
import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js';
|
import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js';
|
||||||
import { ChatEditingSessionChangeType, ChatEditingSessionState, ChatEditKind, IChatEditingSession, WorkingSetDisplayMetadata, WorkingSetEntryState } from '../../common/chatEditingService.js';
|
import { ChatEditingSessionChangeType, ChatEditingSessionState, ChatEditKind, IChatEditingSession, WorkingSetDisplayMetadata, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../../common/chatEditingService.js';
|
||||||
import { IChatResponseModel } from '../../common/chatModel.js';
|
import { IChatResponseModel } from '../../common/chatModel.js';
|
||||||
import { IChatWidgetService } from '../chat.js';
|
import { IChatWidgetService } from '../chat.js';
|
||||||
import { ChatEditingMultiDiffSourceResolver } from './chatEditingService.js';
|
import { ChatEditingMultiDiffSourceResolver } from './chatEditingService.js';
|
||||||
|
@ -328,7 +328,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
|
||||||
this._entriesObs.set(entriesArr, undefined);
|
this._entriesObs.set(entriesArr, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(...uris: URI[]): void {
|
remove(reason: WorkingSetEntryRemovalReason, ...uris: URI[]): void {
|
||||||
this._assertNotDisposed();
|
this._assertNotDisposed();
|
||||||
|
|
||||||
let didRemoveUris = false;
|
let didRemoveUris = false;
|
||||||
|
@ -338,7 +338,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
didRemoveUris = this._workingSet.delete(uri) || didRemoveUris;
|
didRemoveUris = this._workingSet.delete(uri) || didRemoveUris;
|
||||||
if (state.state === WorkingSetEntryState.Transient || state.state === WorkingSetEntryState.Suggested) {
|
if (reason === WorkingSetEntryRemovalReason.User && (state.state === WorkingSetEntryState.Transient || state.state === WorkingSetEntryState.Suggested)) {
|
||||||
this._removedTransientEntries.add(uri);
|
this._removedTransientEntries.add(uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1085,22 +1085,36 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
|
||||||
// The user tried to attach too many files, we have to drop anything after the limit
|
// The user tried to attach too many files, we have to drop anything after the limit
|
||||||
const entriesToPreserve: IChatCollapsibleListItem[] = [];
|
const entriesToPreserve: IChatCollapsibleListItem[] = [];
|
||||||
const newEntries: IChatCollapsibleListItem[] = [];
|
const newEntries: IChatCollapsibleListItem[] = [];
|
||||||
|
const suggestedFiles: IChatCollapsibleListItem[] = [];
|
||||||
for (let i = 0; i < entries.length; i += 1) {
|
for (let i = 0; i < entries.length; i += 1) {
|
||||||
const entry = entries[i];
|
const entry = entries[i];
|
||||||
// If this entry was here earlier and is still here, we should prioritize preserving it
|
if (entry.kind !== 'reference' || !URI.isUri(entry.reference)) {
|
||||||
// so that nothing existing gets evicted
|
continue;
|
||||||
const currentEntryUri = entry.kind === 'reference' && URI.isUri(entry.reference) ? entry.reference : undefined;
|
}
|
||||||
if (this._combinedChatEditWorkingSetEntries.find((e) => e.toString() === currentEntryUri?.toString()) && remainingFileEntriesBudget > 0) {
|
const currentEntryUri = entry.reference;
|
||||||
entriesToPreserve.push(entry);
|
if (entry.state === WorkingSetEntryState.Suggested) {
|
||||||
remainingFileEntriesBudget -= 1;
|
// Keep track of suggested files for now, they should not take precedence over newly added files
|
||||||
|
suggestedFiles.push(entry);
|
||||||
|
} else if (this._combinedChatEditWorkingSetEntries.find((e) => e.toString() === currentEntryUri?.toString())) {
|
||||||
|
// If this entry was here earlier and is still here, we should prioritize preserving it
|
||||||
|
// so that nothing existing gets evicted
|
||||||
|
if (remainingFileEntriesBudget > 0) {
|
||||||
|
entriesToPreserve.push(entry);
|
||||||
|
remainingFileEntriesBudget -= 1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
newEntries.push(entry);
|
newEntries.push(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const newEntriesThatFit = newEntries.slice(0, remainingFileEntriesBudget);
|
const newEntriesThatFit = remainingFileEntriesBudget > 0 ? newEntries.slice(0, remainingFileEntriesBudget) : [];
|
||||||
entries = [...entriesToPreserve, ...newEntriesThatFit];
|
|
||||||
remainingFileEntriesBudget -= newEntriesThatFit.length;
|
remainingFileEntriesBudget -= newEntriesThatFit.length;
|
||||||
|
const suggestedFilesThatFit = remainingFileEntriesBudget > 0 ? suggestedFiles.slice(0, remainingFileEntriesBudget) : [];
|
||||||
|
// Intentional: to make bad suggestions less annoying,
|
||||||
|
// here we don't count the suggested files against the budget,
|
||||||
|
// so that the Add Files button remains enabled and the user can easily
|
||||||
|
// override the suggestions with their own manual file selections
|
||||||
|
entries = [...entriesToPreserve, ...newEntriesThatFit, ...suggestedFilesThatFit];
|
||||||
}
|
}
|
||||||
if (entries.length > 1) {
|
if (entries.length > 1) {
|
||||||
overviewText.textContent += ' ' + localize('chatEditingSession.manyFiles', '({0} files)', entries.length);
|
overviewText.textContent += ' ' + localize('chatEditingSession.manyFiles', '({0} files)', entries.length);
|
||||||
|
@ -1184,6 +1198,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
|
||||||
supportIcons: true,
|
supportIcons: true,
|
||||||
secondary: true
|
secondary: true
|
||||||
}));
|
}));
|
||||||
|
// Disable the button if the entries that are not suggested exceed the budget
|
||||||
button.enabled = remainingFileEntriesBudget > 0;
|
button.enabled = remainingFileEntriesBudget > 0;
|
||||||
button.label = localize('chatAddFiles', '{0} Add Files...', '$(add)');
|
button.label = localize('chatAddFiles', '{0} Add Files...', '$(add)');
|
||||||
button.setTitle(button.enabled ? localize('addFiles.label', 'Add files to your working set') : localize('addFilesDisabled.label', 'You have reached the maximum number of files that can be added to the working set.'));
|
button.setTitle(button.enabled ? localize('addFiles.label', 'Add files to your working set') : localize('addFilesDisabled.label', 'You have reached the maximum number of files that can be added to the working set.'));
|
||||||
|
|
|
@ -35,7 +35,7 @@ import { asCssVariable } from '../../../../platform/theme/common/colorUtils.js';
|
||||||
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
|
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
|
||||||
import { ChatAgentLocation, IChatAgentCommand, IChatAgentData, IChatAgentService, IChatWelcomeMessageContent, isChatWelcomeMessageContent } from '../common/chatAgents.js';
|
import { ChatAgentLocation, IChatAgentCommand, IChatAgentData, IChatAgentService, IChatWelcomeMessageContent, isChatWelcomeMessageContent } from '../common/chatAgents.js';
|
||||||
import { ChatContextKeys } from '../common/chatContextKeys.js';
|
import { ChatContextKeys } from '../common/chatContextKeys.js';
|
||||||
import { IChatEditingService, IChatEditingSession, WorkingSetEntryState } from '../common/chatEditingService.js';
|
import { IChatEditingService, IChatEditingSession, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../common/chatEditingService.js';
|
||||||
import { IChatModel, IChatRequestVariableEntry, IChatResponseModel } from '../common/chatModel.js';
|
import { IChatModel, IChatRequestVariableEntry, IChatResponseModel } from '../common/chatModel.js';
|
||||||
import { ChatRequestAgentPart, IParsedChatRequest, chatAgentLeader, chatSubcommandLeader, formatChatQuestion } from '../common/chatParserTypes.js';
|
import { ChatRequestAgentPart, IParsedChatRequest, chatAgentLeader, chatSubcommandLeader, formatChatQuestion } from '../common/chatParserTypes.js';
|
||||||
import { ChatRequestParser } from '../common/chatRequestParser.js';
|
import { ChatRequestParser } from '../common/chatRequestParser.js';
|
||||||
|
@ -1052,7 +1052,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
|
||||||
actualSize: number;
|
actualSize: number;
|
||||||
};
|
};
|
||||||
this.telemetryService.publicLog2<ChatEditingWorkingSetEvent, ChatEditingWorkingSetClassification>('chatEditing/workingSetSize', { originalSize: this.inputPart.attemptedWorkingSetEntriesCount, actualSize: uniqueWorkingSetEntries.size });
|
this.telemetryService.publicLog2<ChatEditingWorkingSetEvent, ChatEditingWorkingSetClassification>('chatEditing/workingSetSize', { originalSize: this.inputPart.attemptedWorkingSetEntriesCount, actualSize: uniqueWorkingSetEntries.size });
|
||||||
currentEditingSession?.remove(...unconfirmedSuggestions);
|
currentEditingSession?.remove(WorkingSetEntryRemovalReason.Programmatic, ...unconfirmedSuggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await this.chatService.sendRequest(this.viewModel.sessionId, input, {
|
const result = await this.chatService.sendRequest(this.viewModel.sessionId, input, {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { ResourceSet } from '../../../../../base/common/map.js';
|
||||||
import { URI } from '../../../../../base/common/uri.js';
|
import { URI } from '../../../../../base/common/uri.js';
|
||||||
import { localize } from '../../../../../nls.js';
|
import { localize } from '../../../../../nls.js';
|
||||||
import { IWorkbenchContribution } from '../../../../common/contributions.js';
|
import { IWorkbenchContribution } from '../../../../common/contributions.js';
|
||||||
import { ChatEditingSessionChangeType, IChatEditingService, WorkingSetEntryState } from '../../common/chatEditingService.js';
|
import { ChatEditingSessionChangeType, IChatEditingService, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../../common/chatEditingService.js';
|
||||||
import { IChatWidgetService } from '../chat.js';
|
import { IChatWidgetService } from '../chat.js';
|
||||||
|
|
||||||
export class ChatRelatedFilesContribution extends Disposable implements IWorkbenchContribution {
|
export class ChatRelatedFilesContribution extends Disposable implements IWorkbenchContribution {
|
||||||
|
@ -80,7 +80,7 @@ export class ChatRelatedFilesContribution extends Disposable implements IWorkben
|
||||||
existingSuggestedEntriesToRemove.push(entry[0]);
|
existingSuggestedEntriesToRemove.push(entry[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentEditingSession?.remove(...existingSuggestedEntriesToRemove);
|
currentEditingSession?.remove(WorkingSetEntryRemovalReason.Programmatic, ...existingSuggestedEntriesToRemove);
|
||||||
|
|
||||||
// Add the new related file suggestions to the working set
|
// Add the new related file suggestions to the working set
|
||||||
for (const file of newSuggestions) {
|
for (const file of newSuggestions) {
|
||||||
|
|
|
@ -79,7 +79,7 @@ export interface IChatEditingSession {
|
||||||
readonly isVisible: boolean;
|
readonly isVisible: boolean;
|
||||||
addFileToWorkingSet(uri: URI, description?: string, kind?: WorkingSetEntryState.Transient | WorkingSetEntryState.Suggested): void;
|
addFileToWorkingSet(uri: URI, description?: string, kind?: WorkingSetEntryState.Transient | WorkingSetEntryState.Suggested): void;
|
||||||
show(): Promise<void>;
|
show(): Promise<void>;
|
||||||
remove(...uris: URI[]): void;
|
remove(reason: WorkingSetEntryRemovalReason, ...uris: URI[]): void;
|
||||||
accept(...uris: URI[]): Promise<void>;
|
accept(...uris: URI[]): Promise<void>;
|
||||||
reject(...uris: URI[]): Promise<void>;
|
reject(...uris: URI[]): Promise<void>;
|
||||||
/**
|
/**
|
||||||
|
@ -91,6 +91,11 @@ export interface IChatEditingSession {
|
||||||
redoInteraction(): Promise<void>;
|
redoInteraction(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum WorkingSetEntryRemovalReason {
|
||||||
|
User,
|
||||||
|
Programmatic
|
||||||
|
}
|
||||||
|
|
||||||
export const enum WorkingSetEntryState {
|
export const enum WorkingSetEntryState {
|
||||||
Modified,
|
Modified,
|
||||||
Accepted,
|
Accepted,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче