Не подтверждена Коммит 14ac777e создал по автору Joyce Er's avatar Joyce Er Зафиксировано автором GitHub
Просмотр файлов

feat: support removing files from working set (#230270)

* fix: don't render chat editing session widget in normal chat

* feat: support removing files from working set

* Fix stale imports
владелец 51992f3c
......@@ -20,7 +20,7 @@ import { ChatViewPane } from '../chatViewPane.js';
import { CONTEXT_IN_CHAT_SESSION, CONTEXT_CHAT_ENABLED, CONTEXT_CHAT_EDITING_PARTICIPANT_REGISTERED } from '../../common/chatContextKeys.js';
import { IViewsService } from '../../../../services/views/common/viewsService.js';
import { ChatAgentLocation } from '../../common/chatAgents.js';
import { ChatContextAttachments } from '../contrib/chatContextAttachments.js';
import { ChatContextAttachments } from '../chatWidget.js';
export const ACTION_ID_NEW_CHAT = `workbench.action.chat.newChat`;
......
......@@ -26,8 +26,7 @@ import { AnythingQuickAccessProviderRunOptions } from '../../../../../platform/q
import { IQuickInputService, IQuickPickItem, IQuickPickItemWithResource, IQuickPickSeparator, QuickPickItem } from '../../../../../platform/quickinput/common/quickInput.js';
import { CHAT_CATEGORY } from './chatActions.js';
import { IChatWidget, IChatWidgetService, IQuickChatService, showChatView } from '../chat.js';
import { isQuickChat } from '../chatWidget.js';
import { ChatContextAttachments } from '../contrib/chatContextAttachments.js';
import { ChatContextAttachments, isQuickChat } from '../chatWidget.js';
import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js';
import { CONTEXT_CHAT_LOCATION, CONTEXT_IN_CHAT_INPUT } from '../../common/chatContextKeys.js';
import { IChatEditingService } from '../../common/chatEditingService.js';
......
......@@ -65,7 +65,6 @@ import { ChatResponseAccessibleView } from './chatResponseAccessibleView.js';
import { ChatVariablesService } from './chatVariables.js';
import { ChatWidgetService } from './chatWidget.js';
import { ChatCodeBlockContextProviderService } from './codeBlockContextProviderService.js';
import './contrib/chatContextAttachments.js';
import './contrib/chatInputCompletions.js';
import './contrib/chatInputEditorContrib.js';
import './contrib/chatInputEditorHover.js';
......
......@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Codicon } from '../../../../base/common/codicons.js';
import { ResourceSet } from '../../../../base/common/map.js';
import { URI } from '../../../../base/common/uri.js';
import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';
import { localize, localize2 } from '../../../../nls.js';
......@@ -11,8 +12,9 @@ import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/c
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
import { EditorActivation } from '../../../../platform/editor/common/editor.js';
import { IEditorService } from '../../../services/editor/common/editorService.js';
import { CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, IChatEditingService, IChatEditingSession, WorkingSetEntryState, chatEditingResourceContextKey, chatEditingWidgetFileStateContextKey, decidedChatEditingResourceContextKey } from '../common/chatEditingService.js';
import { CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, chatEditingResourceContextKey, chatEditingWidgetFileStateContextKey, decidedChatEditingResourceContextKey, IChatEditingService, IChatEditingSession, WorkingSetEntryState } from '../common/chatEditingService.js';
import { IChatWidget, IChatWidgetService } from './chat.js';
import { ChatContextAttachments } from './chatWidget.js';
abstract class WorkingSetAction extends Action2 {
run(accessor: ServicesAccessor, ...args: any[]) {
......@@ -39,6 +41,41 @@ abstract class WorkingSetAction extends Action2 {
abstract runWorkingSetAction(accessor: ServicesAccessor, currentEditingSession: IChatEditingSession, chatWidget: IChatWidget | undefined, ...uris: URI[]): any;
}
registerAction2(class RemoveFileFromWorkingSet extends WorkingSetAction {
constructor() {
super({
id: 'chatEditing.removeFileFromWorkingSet',
title: localize2('removeFileFromWorkingSet', 'Remove File'),
icon: Codicon.close,
menu: [{
id: MenuId.ChatEditingSessionWidgetToolbar,
// when: ContextKeyExpr.false(), // TODO@joyceerhl enable this when attachments are stored as part of the chat input
when: ContextKeyExpr.equals(chatEditingWidgetFileStateContextKey.key, WorkingSetEntryState.Attached),
order: 0,
group: 'navigation'
}],
});
}
async runWorkingSetAction(accessor: ServicesAccessor, currentEditingSession: IChatEditingSession, chatWidget: IChatWidget, ...uris: URI[]): Promise<void> {
// Remove from working set
currentEditingSession.remove(...uris);
// Remove from chat input part
const resourceSet = new ResourceSet(uris);
const newContext = [];
for (const context of chatWidget.input.attachedContext) {
if (!URI.isUri(context.value) || !context.isFile || !resourceSet.has(context.value)) {
newContext.push(context);
}
}
chatWidget.getContrib<ChatContextAttachments>(ChatContextAttachments.ID)?.setContext(true, ...newContext);
}
});
registerAction2(class OpenFileAction extends WorkingSetAction {
constructor() {
super({
......
......@@ -8,6 +8,7 @@ import { CancellationToken, CancellationTokenSource } from '../../../../base/com
import { BugIndicatingError } from '../../../../base/common/errors.js';
import { Emitter } from '../../../../base/common/event.js';
import { Disposable, DisposableStore, IDisposable, IReference, MutableDisposable } from '../../../../base/common/lifecycle.js';
import { ResourceSet } from '../../../../base/common/map.js';
import { derived, IObservable, ITransaction, observableValue, ValueWithChangeEventFromObservable } from '../../../../base/common/observable.js';
import { URI } from '../../../../base/common/uri.js';
import { IBulkEditService } from '../../../../editor/browser/services/bulkEditService.js';
......@@ -386,6 +387,29 @@ class ChatEditingSession extends Disposable implements IChatEditingSession {
super();
}
remove(...uris: URI[]): void {
this._assertNotDisposed();
const workingSetSize = this._workingSet.length;
const urisToRemove = new ResourceSet(uris);
const newWorkingSet = [];
for (const resource of this._workingSet) {
if (urisToRemove.has(resource)) {
continue;
}
newWorkingSet.push(resource);
}
this._workingSet = newWorkingSet;
if (this._workingSet.length === workingSetSize) {
return; // noop
}
this._workingSetObs.set(this._workingSet, undefined);
this._onDidChange.fire();
}
private _assertNotDisposed(): void {
if (this._state.get() === ChatEditingSessionState.Disposed) {
throw new BugIndicatingError(`Cannot access a disposed editing session`);
......
......@@ -872,6 +872,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
modifiedFiles.add(entry.modifiedURI);
return {
reference: entry.modifiedURI,
state: entry.state.get(),
kind: 'reference',
};
}) ?? [];
......
......@@ -19,7 +19,7 @@ import { ChatRequestDynamicVariablePart, ChatRequestToolPart, ChatRequestVariabl
import { IChatContentReference } from '../common/chatService.js';
import { IChatRequestVariableValue, IChatVariableData, IChatVariableResolver, IChatVariableResolverProgress, IChatVariablesService, IDynamicVariable } from '../common/chatVariables.js';
import { IChatWidgetService, showChatView } from './chat.js';
import { ChatContextAttachments } from './contrib/chatContextAttachments.js';
import { ChatContextAttachments } from './chatWidget.js';
import { ChatDynamicVariableModel } from './contrib/chatDynamicVariables.js';
interface IChatData {
......
......@@ -33,7 +33,7 @@ import { TerminalChatController } from '../../terminal/terminalContribChatExport
import { ChatAgentLocation, IChatAgentCommand, IChatAgentData, IChatAgentService, IChatWelcomeMessageContent, isChatWelcomeMessageContent } from '../common/chatAgents.js';
import { CONTEXT_CHAT_INPUT_HAS_AGENT, CONTEXT_CHAT_LOCATION, CONTEXT_CHAT_REQUEST_IN_PROGRESS, CONTEXT_IN_CHAT_SESSION, CONTEXT_IN_QUICK_CHAT, CONTEXT_LAST_ITEM_ID, CONTEXT_PARTICIPANT_SUPPORTS_MODEL_PICKER, CONTEXT_RESPONSE_FILTERED } from '../common/chatContextKeys.js';
import { ChatEditingSessionState, IChatEditingService, IChatEditingSession } from '../common/chatEditingService.js';
import { IChatModel, IChatRequestVariableEntry, IChatResponseModel } from '../common/chatModel.js';
import { IChatModel, IChatRequestVariableEntry, IChatResponseModel, isChatRequestVariableEntry } from '../common/chatModel.js';
import { ChatRequestAgentPart, IParsedChatRequest, chatAgentLeader, chatSubcommandLeader, formatChatQuestion } from '../common/chatParserTypes.js';
import { ChatRequestParser } from '../common/chatRequestParser.js';
import { IChatFollowup, IChatLocationData, IChatService } from '../common/chatService.js';
......@@ -937,7 +937,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
setContext(overwrite: boolean, ...contentReferences: IChatRequestVariableEntry[]) {
this.inputPart.attachContext(overwrite, ...contentReferences);
if (this.chatEditingService.currentEditingSession) {
if (this.chatEditingService.currentEditingSession && this.chatEditingService.currentEditingSession?.chatSessionId === this.viewModel?.sessionId) {
this.renderChatEditingSessionState(this.chatEditingService.currentEditingSession);
}
}
......@@ -1170,3 +1170,67 @@ export class ChatWidgetService implements IChatWidgetService {
);
}
}
export class ChatContextAttachments extends Disposable implements IChatWidgetContrib {
private _attachedContext = new Map<string, IChatRequestVariableEntry>();
public static readonly ID = 'chatContextAttachments';
get id() {
return ChatContextAttachments.ID;
}
constructor(readonly widget: IChatWidget) {
super();
this._register(this.widget.onDidChangeContext(({ removed, added }) => {
removed?.forEach(attachment => this._attachedContext.delete(attachment.id));
added?.forEach(attachment => {
if (!this._attachedContext.has(attachment.id)) {
this._attachedContext.set(attachment.id, attachment);
}
});
}));
this._register(this.widget.onDidSubmitAgent(() => {
this._clearAttachedContext();
}));
}
getInputState(): IChatRequestVariableEntry[] {
return [...this._attachedContext.values()];
}
setInputState(s: unknown): void {
const attachments = Array.isArray(s) ? s.filter(isChatRequestVariableEntry) : [];
this.setContext(true, ...attachments);
}
getContext() {
return new Set(this._attachedContext.keys());
}
setContext(overwrite: boolean, ...attachments: IChatRequestVariableEntry[]) {
if (overwrite) {
this._attachedContext.clear();
}
const newAttachments = [];
for (const attachment of attachments) {
if (!this._attachedContext.has(attachment.id)) {
this._attachedContext.set(attachment.id, attachment);
newAttachments.push(attachment);
}
}
this.widget.setContext(overwrite, ...newAttachments);
}
private _clearAttachedContext() {
this._attachedContext.clear();
}
}
ChatWidget.CONTRIBS.push(ChatContextAttachments);
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from '../../../../../base/common/lifecycle.js';
import { IChatWidget } from '../chat.js';
import { ChatWidget, IChatWidgetContrib } from '../chatWidget.js';
import { IChatRequestVariableEntry, isChatRequestVariableEntry } from '../../common/chatModel.js';
export class ChatContextAttachments extends Disposable implements IChatWidgetContrib {
private _attachedContext = new Map<string, IChatRequestVariableEntry>();
public static readonly ID = 'chatContextAttachments';
get id() {
return ChatContextAttachments.ID;
}
constructor(readonly widget: IChatWidget) {
super();
this._register(this.widget.onDidChangeContext(({ removed, added }) => {
removed?.forEach(attachment => this._attachedContext.delete(attachment.id));
added?.forEach(attachment => {
if (!this._attachedContext.has(attachment.id)) {
this._attachedContext.set(attachment.id, attachment);
}
});
}));
this._register(this.widget.onDidSubmitAgent(() => {
this._clearAttachedContext();
}));
}
getInputState(): IChatRequestVariableEntry[] {
return [...this._attachedContext.values()];
}
setInputState(s: unknown): void {
const attachments = Array.isArray(s) ? s.filter(isChatRequestVariableEntry) : [];
this.setContext(true, ...attachments);
}
getContext() {
return new Set(this._attachedContext.keys());
}
setContext(overwrite: boolean, ...attachments: IChatRequestVariableEntry[]) {
if (overwrite) {
this._attachedContext.clear();
}
const newAttachments = [];
for (const attachment of attachments) {
if (!this._attachedContext.has(attachment.id)) {
this._attachedContext.set(attachment.id, attachment);
newAttachments.push(attachment);
}
}
this.widget.setContext(overwrite, ...newAttachments);
}
private _clearAttachedContext() {
this._attachedContext.clear();
}
}
ChatWidget.CONTRIBS.push(ChatContextAttachments);
......@@ -35,6 +35,7 @@ export interface IChatEditingSession {
readonly entries: IObservable<readonly IModifiedFileEntry[]>;
readonly isVisible: boolean;
show(): Promise<void>;
remove(...uris: URI[]): void;
accept(...uris: URI[]): Promise<void>;
reject(...uris: URI[]): Promise<void>;
/**
......
......@@ -21,7 +21,7 @@ import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase
import { IEditorService } from '../../../../../services/editor/common/editorService.js';
import { IChatWidget, IChatWidgetService } from '../../../../chat/browser/chat.js';
import { ChatInputPart } from '../../../../chat/browser/chatInputPart.js';
import { ChatContextAttachments } from '../../../../chat/browser/contrib/chatContextAttachments.js';
import { ChatContextAttachments } from '../../../../chat/browser/chatWidget.js';
import { ChatDynamicVariableModel } from '../../../../chat/browser/contrib/chatDynamicVariables.js';
import { computeCompletionRanges } from '../../../../chat/browser/contrib/chatInputCompletions.js';
import { ChatAgentLocation, IChatAgentService } from '../../../../chat/common/chatAgents.js';
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать