Path: blob/main/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts
3297 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/4import assert from 'assert';5import { DisposableStore } from '../../../../../base/common/lifecycle.js';6import { Event } from '../../../../../base/common/event.js';7import { mock } from '../../../../../base/test/common/mock.js';8import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';9import { TestDiffProviderFactoryService } from '../../../../../editor/test/browser/diff/testDiffProviderFactoryService.js';10import { IActiveCodeEditor } from '../../../../../editor/browser/editorBrowser.js';11import { IDiffProviderFactoryService } from '../../../../../editor/browser/widget/diffEditor/diffProviderFactoryService.js';12import { Range } from '../../../../../editor/common/core/range.js';13import { ITextModel } from '../../../../../editor/common/model.js';14import { IModelService } from '../../../../../editor/common/services/model.js';15import { instantiateTestCodeEditor } from '../../../../../editor/test/browser/testCodeEditor.js';16import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';17import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js';18import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js';19import { SyncDescriptor } from '../../../../../platform/instantiation/common/descriptors.js';20import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js';21import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';22import { MockContextKeyService } from '../../../../../platform/keybinding/test/common/mockKeybindingService.js';23import { IEditorProgressService, IProgressRunner } from '../../../../../platform/progress/common/progress.js';24import { IViewDescriptorService } from '../../../../common/views.js';25import { AccessibilityVerbositySettingId } from '../../../accessibility/browser/accessibilityConfiguration.js';26import { IChatAccessibilityService, IChatWidgetService } from '../../../chat/browser/chat.js';27import { IChatResponseViewModel } from '../../../chat/common/chatViewModel.js';28import { HunkState } from '../../browser/inlineChatSession.js';29import { IInlineChatSessionService } from '../../browser/inlineChatSessionService.js';30import { InlineChatSessionServiceImpl } from '../../browser/inlineChatSessionServiceImpl.js';31import { workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js';32import { CancellationToken } from '../../../../../base/common/cancellation.js';33import { assertType } from '../../../../../base/common/types.js';34import { EditOperation } from '../../../../../editor/common/core/editOperation.js';35import { Position } from '../../../../../editor/common/core/position.js';36import { IEditorWorkerService } from '../../../../../editor/common/services/editorWorker.js';37import { TestWorkerService } from './testWorkerService.js';38import { IExtensionService, nullExtensionDescription } from '../../../../services/extensions/common/extensions.js';39import { ILogService, NullLogService } from '../../../../../platform/log/common/log.js';40import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';41import { NullTelemetryService } from '../../../../../platform/telemetry/common/telemetryUtils.js';42import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js';43import { ChatWidgetService } from '../../../chat/browser/chatWidget.js';44import { IChatService } from '../../../chat/common/chatService.js';45import { ChatService } from '../../../chat/common/chatServiceImpl.js';46import { IChatSlashCommandService, ChatSlashCommandService } from '../../../chat/common/chatSlashCommands.js';47import { IChatVariablesService } from '../../../chat/common/chatVariables.js';48import { IChatWidgetHistoryService, ChatWidgetHistoryService } from '../../../chat/common/chatWidgetHistoryService.js';49import { IViewsService } from '../../../../services/views/common/viewsService.js';50import { TestExtensionService, TestContextService } from '../../../../test/common/workbenchTestServices.js';51import { IChatAgentService, ChatAgentService } from '../../../chat/common/chatAgents.js';52import { ChatVariablesService } from '../../../chat/browser/chatVariables.js';53import { ICommandService } from '../../../../../platform/commands/common/commands.js';54import { TestCommandService } from '../../../../../editor/test/browser/editorTestServices.js';55import { IAccessibleViewService } from '../../../../../platform/accessibility/browser/accessibleView.js';56import { IWorkbenchAssignmentService } from '../../../../services/assignment/common/assignmentService.js';57import { NullWorkbenchAssignmentService } from '../../../../services/assignment/test/common/nullAssignmentService.js';58import { ILanguageModelToolsService } from '../../../chat/common/languageModelToolsService.js';59import { MockLanguageModelToolsService } from '../../../chat/test/common/mockLanguageModelToolsService.js';60import { IChatRequestModel } from '../../../chat/common/chatModel.js';61import { assertSnapshot } from '../../../../../base/test/common/snapshot.js';62import { IObservable, constObservable } from '../../../../../base/common/observable.js';63import { IChatEditingService, IChatEditingSession } from '../../../chat/common/chatEditingService.js';64import { ChatAgentLocation, ChatModeKind } from '../../../chat/common/constants.js';65import { ChatTransferService, IChatTransferService } from '../../../chat/common/chatTransferService.js';66import { NullLanguageModelsService } from '../../../chat/test/common/languageModels.js';67import { ILanguageModelsService } from '../../../chat/common/languageModels.js';68import { IMcpService } from '../../../mcp/common/mcpTypes.js';69import { TestMcpService } from '../../../mcp/test/common/testMcpService.js';70import { IChatSessionsService } from '../../../chat/common/chatSessionsService.js';71import { ChatSessionsService } from '../../../chat/browser/chatSessions.contribution.js';7273suite('InlineChatSession', function () {7475const store = new DisposableStore();76let editor: IActiveCodeEditor;77let model: ITextModel;78let instaService: TestInstantiationService;7980let inlineChatSessionService: IInlineChatSessionService;8182setup(function () {83const contextKeyService = new MockContextKeyService();848586const serviceCollection = new ServiceCollection(87[IConfigurationService, new TestConfigurationService()],88[IChatVariablesService, new SyncDescriptor(ChatVariablesService)],89[ILogService, new NullLogService()],90[ITelemetryService, NullTelemetryService],91[IExtensionService, new TestExtensionService()],92[IContextKeyService, new MockContextKeyService()],93[IViewsService, new TestExtensionService()],94[IWorkspaceContextService, new TestContextService()],95[IChatWidgetHistoryService, new SyncDescriptor(ChatWidgetHistoryService)],96[IChatWidgetService, new SyncDescriptor(ChatWidgetService)],97[IChatSlashCommandService, new SyncDescriptor(ChatSlashCommandService)],98[IChatTransferService, new SyncDescriptor(ChatTransferService)],99[IChatSessionsService, new SyncDescriptor(ChatSessionsService)],100[IChatService, new SyncDescriptor(ChatService)],101[IEditorWorkerService, new SyncDescriptor(TestWorkerService)],102[IChatAgentService, new SyncDescriptor(ChatAgentService)],103[IContextKeyService, contextKeyService],104[IDiffProviderFactoryService, new SyncDescriptor(TestDiffProviderFactoryService)],105[ILanguageModelsService, new SyncDescriptor(NullLanguageModelsService)],106[IInlineChatSessionService, new SyncDescriptor(InlineChatSessionServiceImpl)],107[ICommandService, new SyncDescriptor(TestCommandService)],108[ILanguageModelToolsService, new MockLanguageModelToolsService()],109[IMcpService, new TestMcpService()],110[IEditorProgressService, new class extends mock<IEditorProgressService>() {111override show(total: unknown, delay?: unknown): IProgressRunner {112return {113total() { },114worked(value) { },115done() { },116};117}118}],119[IChatEditingService, new class extends mock<IChatEditingService>() {120override editingSessionsObs: IObservable<readonly IChatEditingSession[]> = constObservable([]);121}],122[IChatAccessibilityService, new class extends mock<IChatAccessibilityService>() {123override acceptResponse(response: IChatResponseViewModel | undefined, requestId: number): void { }124override acceptRequest(): number { return -1; }125override acceptElicitation(): void { }126}],127[IAccessibleViewService, new class extends mock<IAccessibleViewService>() {128override getOpenAriaHint(verbositySettingKey: AccessibilityVerbositySettingId): string | null {129return null;130}131}],132[IConfigurationService, new TestConfigurationService()],133[IViewDescriptorService, new class extends mock<IViewDescriptorService>() {134override onDidChangeLocation = Event.None;135}],136[IWorkbenchAssignmentService, new NullWorkbenchAssignmentService()]137);138139140141instaService = store.add(workbenchInstantiationService(undefined, store).createChild(serviceCollection));142inlineChatSessionService = store.add(instaService.get(IInlineChatSessionService));143store.add(instaService.get(IChatSessionsService) as ChatSessionsService); // Needs to be disposed in between test runs to clear extensionPoint contribution144145instaService.get(IChatAgentService).registerDynamicAgent({146extensionId: nullExtensionDescription.identifier,147extensionVersion: undefined,148publisherDisplayName: '',149extensionDisplayName: '',150extensionPublisherId: '',151id: 'testAgent',152name: 'testAgent',153isDefault: true,154locations: [ChatAgentLocation.Editor],155modes: [ChatModeKind.Ask],156metadata: {},157slashCommands: [],158disambiguation: [],159}, {160async invoke() {161return {};162}163});164165166store.add(instaService.get(IEditorWorkerService) as TestWorkerService);167model = store.add(instaService.get(IModelService).createModel('one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven', null));168editor = store.add(instantiateTestCodeEditor(instaService, model));169});170171teardown(function () {172store.clear();173});174175ensureNoDisposablesAreLeakedInTestSuite();176177async function makeEditAsAi(edit: EditOperation | EditOperation[]) {178const session = inlineChatSessionService.getSession(editor, editor.getModel()!.uri);179assertType(session);180session.hunkData.ignoreTextModelNChanges = true;181try {182editor.executeEdits('test', Array.isArray(edit) ? edit : [edit]);183} finally {184session.hunkData.ignoreTextModelNChanges = false;185}186await session.hunkData.recompute({ applied: 0, sha1: 'fakeSha1' });187}188189function makeEdit(edit: EditOperation | EditOperation[]) {190editor.executeEdits('test', Array.isArray(edit) ? edit : [edit]);191}192193test('Create, release', async function () {194195const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);196assertType(session);197inlineChatSessionService.releaseSession(session);198});199200test('HunkData, info', async function () {201202const decorationCountThen = model.getAllDecorations().length;203204const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);205assertType(session);206assert.ok(session.textModelN === model);207208await makeEditAsAi(EditOperation.insert(new Position(1, 1), 'AI_EDIT\n'));209210211assert.strictEqual(session.hunkData.size, 1);212let [hunk] = session.hunkData.getInfo();213assertType(hunk);214215assert.ok(!session.textModel0.equalsTextBuffer(session.textModelN.getTextBuffer()));216assert.strictEqual(hunk.getState(), HunkState.Pending);217assert.ok(hunk.getRangesN()[0].equalsRange({ startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 8 }));218219await makeEditAsAi(EditOperation.insert(new Position(1, 3), 'foobar'));220[hunk] = session.hunkData.getInfo();221assert.ok(hunk.getRangesN()[0].equalsRange({ startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 14 }));222223inlineChatSessionService.releaseSession(session);224225assert.strictEqual(model.getAllDecorations().length, decorationCountThen); // no leaked decorations!226});227228test('HunkData, accept', async function () {229230const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);231assertType(session);232233await makeEditAsAi([EditOperation.insert(new Position(1, 1), 'AI_EDIT\n'), EditOperation.insert(new Position(10, 1), 'AI_EDIT\n')]);234235assert.strictEqual(session.hunkData.size, 2);236assert.ok(!session.textModel0.equalsTextBuffer(session.textModelN.getTextBuffer()));237238for (const hunk of session.hunkData.getInfo()) {239assertType(hunk);240assert.strictEqual(hunk.getState(), HunkState.Pending);241hunk.acceptChanges();242assert.strictEqual(hunk.getState(), HunkState.Accepted);243}244245assert.strictEqual(session.textModel0.getValue(), session.textModelN.getValue());246inlineChatSessionService.releaseSession(session);247});248249test('HunkData, reject', async function () {250251const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);252assertType(session);253254await makeEditAsAi([EditOperation.insert(new Position(1, 1), 'AI_EDIT\n'), EditOperation.insert(new Position(10, 1), 'AI_EDIT\n')]);255256assert.strictEqual(session.hunkData.size, 2);257assert.ok(!session.textModel0.equalsTextBuffer(session.textModelN.getTextBuffer()));258259for (const hunk of session.hunkData.getInfo()) {260assertType(hunk);261assert.strictEqual(hunk.getState(), HunkState.Pending);262hunk.discardChanges();263assert.strictEqual(hunk.getState(), HunkState.Rejected);264}265266assert.strictEqual(session.textModel0.getValue(), session.textModelN.getValue());267inlineChatSessionService.releaseSession(session);268});269270test('HunkData, N rounds', async function () {271272model.setValue('one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven\ntwelwe\nthirteen\nfourteen\nfifteen\nsixteen\nseventeen\neighteen\nnineteen\n');273274const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);275assertType(session);276277assert.ok(session.textModel0.equalsTextBuffer(session.textModelN.getTextBuffer()));278279assert.strictEqual(session.hunkData.size, 0);280281// ROUND #1282await makeEditAsAi([283EditOperation.insert(new Position(1, 1), 'AI1'),284EditOperation.insert(new Position(4, 1), 'AI2'),285EditOperation.insert(new Position(19, 1), 'AI3')286]);287288assert.strictEqual(session.hunkData.size, 2); // AI1, AI2 are merged into one hunk, AI3 is a separate hunk289290let [first, second] = session.hunkData.getInfo();291292assert.ok(model.getValueInRange(first.getRangesN()[0]).includes('AI1'));293assert.ok(model.getValueInRange(first.getRangesN()[0]).includes('AI2'));294assert.ok(model.getValueInRange(second.getRangesN()[0]).includes('AI3'));295296assert.ok(!session.textModel0.getValueInRange(first.getRangesN()[0]).includes('AI1'));297assert.ok(!session.textModel0.getValueInRange(first.getRangesN()[0]).includes('AI2'));298assert.ok(!session.textModel0.getValueInRange(second.getRangesN()[0]).includes('AI3'));299300first.acceptChanges();301assert.ok(session.textModel0.getValueInRange(first.getRangesN()[0]).includes('AI1'));302assert.ok(session.textModel0.getValueInRange(first.getRangesN()[0]).includes('AI2'));303assert.ok(!session.textModel0.getValueInRange(second.getRangesN()[0]).includes('AI3'));304305306// ROUND #2307await makeEditAsAi([308EditOperation.insert(new Position(7, 1), 'AI4'),309]);310assert.strictEqual(session.hunkData.size, 2);311312[first, second] = session.hunkData.getInfo();313assert.ok(model.getValueInRange(first.getRangesN()[0]).includes('AI4')); // the new hunk (in line-order)314assert.ok(model.getValueInRange(second.getRangesN()[0]).includes('AI3')); // the previous hunk remains315316inlineChatSessionService.releaseSession(session);317});318319test('HunkData, (mirror) edit before', async function () {320321const lines = ['one', 'two', 'three'];322model.setValue(lines.join('\n'));323const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);324assertType(session);325326await makeEditAsAi([EditOperation.insert(new Position(3, 1), 'AI WAS HERE\n')]);327assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'AI WAS HERE', 'three'].join('\n'));328assert.strictEqual(session.textModel0.getValue(), lines.join('\n'));329330makeEdit([EditOperation.replace(new Range(1, 1, 1, 4), 'ONE')]);331assert.strictEqual(session.textModelN.getValue(), ['ONE', 'two', 'AI WAS HERE', 'three'].join('\n'));332assert.strictEqual(session.textModel0.getValue(), ['ONE', 'two', 'three'].join('\n'));333});334335test('HunkData, (mirror) edit after', async function () {336337const lines = ['one', 'two', 'three', 'four', 'five'];338model.setValue(lines.join('\n'));339340const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);341assertType(session);342343await makeEditAsAi([EditOperation.insert(new Position(3, 1), 'AI_EDIT\n')]);344345assert.strictEqual(session.hunkData.size, 1);346const [hunk] = session.hunkData.getInfo();347348makeEdit([EditOperation.insert(new Position(1, 1), 'USER1')]);349assert.strictEqual(session.textModelN.getValue(), ['USER1one', 'two', 'AI_EDIT', 'three', 'four', 'five'].join('\n'));350assert.strictEqual(session.textModel0.getValue(), ['USER1one', 'two', 'three', 'four', 'five'].join('\n'));351352makeEdit([EditOperation.insert(new Position(5, 1), 'USER2')]);353assert.strictEqual(session.textModelN.getValue(), ['USER1one', 'two', 'AI_EDIT', 'three', 'USER2four', 'five'].join('\n'));354assert.strictEqual(session.textModel0.getValue(), ['USER1one', 'two', 'three', 'USER2four', 'five'].join('\n'));355356hunk.acceptChanges();357assert.strictEqual(session.textModelN.getValue(), ['USER1one', 'two', 'AI_EDIT', 'three', 'USER2four', 'five'].join('\n'));358assert.strictEqual(session.textModel0.getValue(), ['USER1one', 'two', 'AI_EDIT', 'three', 'USER2four', 'five'].join('\n'));359});360361test('HunkData, (mirror) edit inside ', async function () {362363const lines = ['one', 'two', 'three'];364model.setValue(lines.join('\n'));365const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);366assertType(session);367368await makeEditAsAi([EditOperation.insert(new Position(3, 1), 'AI WAS HERE\n')]);369assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'AI WAS HERE', 'three'].join('\n'));370assert.strictEqual(session.textModel0.getValue(), lines.join('\n'));371372makeEdit([EditOperation.replace(new Range(3, 4, 3, 7), 'wwaaassss')]);373assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'AI wwaaassss HERE', 'three'].join('\n'));374assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three'].join('\n'));375});376377test('HunkData, (mirror) edit after dicard ', async function () {378379const lines = ['one', 'two', 'three'];380model.setValue(lines.join('\n'));381const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);382assertType(session);383384await makeEditAsAi([EditOperation.insert(new Position(3, 1), 'AI WAS HERE\n')]);385assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'AI WAS HERE', 'three'].join('\n'));386assert.strictEqual(session.textModel0.getValue(), lines.join('\n'));387388assert.strictEqual(session.hunkData.size, 1);389const [hunk] = session.hunkData.getInfo();390hunk.discardChanges();391assert.strictEqual(session.textModelN.getValue(), lines.join('\n'));392assert.strictEqual(session.textModel0.getValue(), lines.join('\n'));393394makeEdit([EditOperation.replace(new Range(3, 4, 3, 6), '3333')]);395assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'thr3333'].join('\n'));396assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'thr3333'].join('\n'));397});398399test('HunkData, (mirror) edit after, multi turn', async function () {400401const lines = ['one', 'two', 'three', 'four', 'five'];402model.setValue(lines.join('\n'));403404const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);405assertType(session);406407await makeEditAsAi([EditOperation.insert(new Position(3, 1), 'AI_EDIT\n')]);408409assert.strictEqual(session.hunkData.size, 1);410411makeEdit([EditOperation.insert(new Position(5, 1), 'FOO')]);412assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'AI_EDIT', 'three', 'FOOfour', 'five'].join('\n'));413assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three', 'FOOfour', 'five'].join('\n'));414415await makeEditAsAi([EditOperation.insert(new Position(2, 4), ' zwei')]);416assert.strictEqual(session.hunkData.size, 1);417418assert.strictEqual(session.textModelN.getValue(), ['one', 'two zwei', 'AI_EDIT', 'three', 'FOOfour', 'five'].join('\n'));419assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three', 'FOOfour', 'five'].join('\n'));420421makeEdit([EditOperation.replace(new Range(6, 3, 6, 5), 'vefivefi')]);422assert.strictEqual(session.textModelN.getValue(), ['one', 'two zwei', 'AI_EDIT', 'three', 'FOOfour', 'fivefivefi'].join('\n'));423assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three', 'FOOfour', 'fivefivefi'].join('\n'));424});425426test('HunkData, (mirror) edit after, multi turn 2', async function () {427428const lines = ['one', 'two', 'three', 'four', 'five'];429model.setValue(lines.join('\n'));430431const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);432assertType(session);433434await makeEditAsAi([EditOperation.insert(new Position(3, 1), 'AI_EDIT\n')]);435436assert.strictEqual(session.hunkData.size, 1);437438makeEdit([EditOperation.insert(new Position(5, 1), 'FOO')]);439assert.strictEqual(session.textModelN.getValue(), ['one', 'two', 'AI_EDIT', 'three', 'FOOfour', 'five'].join('\n'));440assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three', 'FOOfour', 'five'].join('\n'));441442await makeEditAsAi([EditOperation.insert(new Position(2, 4), 'zwei')]);443assert.strictEqual(session.hunkData.size, 1);444445assert.strictEqual(session.textModelN.getValue(), ['one', 'twozwei', 'AI_EDIT', 'three', 'FOOfour', 'five'].join('\n'));446assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three', 'FOOfour', 'five'].join('\n'));447448makeEdit([EditOperation.replace(new Range(6, 3, 6, 5), 'vefivefi')]);449assert.strictEqual(session.textModelN.getValue(), ['one', 'twozwei', 'AI_EDIT', 'three', 'FOOfour', 'fivefivefi'].join('\n'));450assert.strictEqual(session.textModel0.getValue(), ['one', 'two', 'three', 'FOOfour', 'fivefivefi'].join('\n'));451452session.hunkData.getInfo()[0].acceptChanges();453assert.strictEqual(session.textModelN.getValue(), session.textModel0.getValue());454455makeEdit([EditOperation.replace(new Range(1, 1, 1, 1), 'done')]);456assert.strictEqual(session.textModelN.getValue(), session.textModel0.getValue());457});458459test('HunkData, accept, discardAll', async function () {460461const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);462assertType(session);463464await makeEditAsAi([EditOperation.insert(new Position(1, 1), 'AI_EDIT\n'), EditOperation.insert(new Position(10, 1), 'AI_EDIT\n')]);465466assert.strictEqual(session.hunkData.size, 2);467assert.ok(!session.textModel0.equalsTextBuffer(session.textModelN.getTextBuffer()));468469const textModeNNow = session.textModelN.getValue();470471session.hunkData.getInfo()[0].acceptChanges();472assert.strictEqual(textModeNNow, session.textModelN.getValue());473474session.hunkData.discardAll(); // all remaining475assert.strictEqual(session.textModelN.getValue(), 'AI_EDIT\none\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven');476assert.strictEqual(session.textModelN.getValue(), session.textModel0.getValue());477478inlineChatSessionService.releaseSession(session);479});480481test('HunkData, discardAll return undo edits', async function () {482483const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);484assertType(session);485486await makeEditAsAi([EditOperation.insert(new Position(1, 1), 'AI_EDIT\n'), EditOperation.insert(new Position(10, 1), 'AI_EDIT\n')]);487488assert.strictEqual(session.hunkData.size, 2);489assert.ok(!session.textModel0.equalsTextBuffer(session.textModelN.getTextBuffer()));490491const textModeNNow = session.textModelN.getValue();492493session.hunkData.getInfo()[0].acceptChanges();494assert.strictEqual(textModeNNow, session.textModelN.getValue());495496const undoEdits = session.hunkData.discardAll(); // all remaining497assert.strictEqual(session.textModelN.getValue(), 'AI_EDIT\none\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten\neleven');498assert.strictEqual(session.textModelN.getValue(), session.textModel0.getValue());499500// undo the discards501session.textModelN.pushEditOperations(null, undoEdits, () => null);502assert.strictEqual(textModeNNow, session.textModelN.getValue());503504inlineChatSessionService.releaseSession(session);505});506507test('Pressing Escape after inline chat errored with "response filtered" leaves document dirty #7764', async function () {508509const origValue = `class Foo {510private onError(error: string): void {511if (/The request timed out|The network connection was lost/i.test(error)) {512return;513}514515error = error.replace(/See https:\/\/github\.com\/Squirrel\/Squirrel\.Mac\/issues\/182 for more information/, 'This might mean the application was put on quarantine by macOS. See [this link](https://github.com/microsoft/vscode/issues/7426#issuecomment-425093469) for more information');516517this.notificationService.notify({518severity: Severity.Error,519message: error,520source: nls.localize('update service', "Update Service"),521});522}523}`;524model.setValue(origValue);525526const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);527assertType(session);528529const fakeRequest = new class extends mock<IChatRequestModel>() {530override get id() { return 'one'; }531};532session.markModelVersion(fakeRequest);533534assert.strictEqual(editor.getModel().getLineCount(), 15);535536await makeEditAsAi([EditOperation.replace(new Range(7, 1, 7, Number.MAX_SAFE_INTEGER), `error = error.replace(537/See https:\/\/github\.com\/Squirrel\/Squirrel\.Mac\/issues\/182 for more information/,538'This might mean the application was put on quarantine by macOS. See [this link](https://github.com/microsoft/vscode/issues/7426#issuecomment-425093469) for more information'539);`)]);540541assert.strictEqual(editor.getModel().getLineCount(), 18);542543// called when a response errors out544await session.undoChangesUntil(fakeRequest.id);545await session.hunkData.recompute({ applied: 0, sha1: 'fakeSha1' }, undefined);546547assert.strictEqual(editor.getModel().getValue(), origValue);548549session.hunkData.discardAll(); // called when dimissing the session550assert.strictEqual(editor.getModel().getValue(), origValue);551});552553test('Apply Code\'s preview should be easier to undo/esc #7537', async function () {554model.setValue(`export function fib(n) {555if (n <= 0) return 0;556if (n === 1) return 0;557if (n === 2) return 1;558return fib(n - 1) + fib(n - 2);559}`);560const session = await inlineChatSessionService.createSession(editor, {}, CancellationToken.None);561assertType(session);562563await makeEditAsAi([EditOperation.replace(new Range(5, 1, 6, Number.MAX_SAFE_INTEGER), `564let a = 0, b = 1, c;565for (let i = 3; i <= n; i++) {566c = a + b;567a = b;568b = c;569}570return b;571}`)]);572573assert.strictEqual(session.hunkData.size, 1);574assert.strictEqual(session.hunkData.pending, 1);575assert.ok(session.hunkData.getInfo().every(d => d.getState() === HunkState.Pending));576577await assertSnapshot(editor.getModel().getValue(), { name: '1' });578579await model.undo();580await assertSnapshot(editor.getModel().getValue(), { name: '2' });581582// overlapping edits (even UNDO) mark edits as accepted583assert.strictEqual(session.hunkData.size, 1);584assert.strictEqual(session.hunkData.pending, 0);585assert.ok(session.hunkData.getInfo().every(d => d.getState() === HunkState.Accepted));586587// no further change when discarding588session.hunkData.discardAll(); // CANCEL589await assertSnapshot(editor.getModel().getValue(), { name: '2' });590});591592});593594595