Skip to content

Commit 8c6f7df

Browse files
authored
Merge pull request microsoft#182494 from microsoft/joh/ridiculous-herring
joh/ridiculous herring
2 parents b025273 + bd2d407 commit 8c6f7df

File tree

4 files changed

+62
-76
lines changed

4 files changed

+62
-76
lines changed

src/vs/workbench/contrib/interactiveEditor/browser/interactiveEditorController.ts

Lines changed: 41 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
77
import { Barrier, raceCancellationError } from 'vs/base/common/async';
88
import { CancellationTokenSource } from 'vs/base/common/cancellation';
9+
import { toErrorMessage } from 'vs/base/common/errorMessage';
910
import { Emitter, Event } from 'vs/base/common/event';
1011
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
1112
import { isEqual } from 'vs/base/common/resources';
@@ -24,6 +25,7 @@ import { InlineCompletionsController } from 'vs/editor/contrib/inlineCompletions
2425
import { localize } from 'vs/nls';
2526
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
2627
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
28+
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
2729
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
2830
import { ILogService } from 'vs/platform/log/common/log';
2931
import { EditResponse, EmptyResponse, ErrorResponse, IInteractiveEditorSessionService, MarkdownResponse, Session, SessionExchange } from 'vs/workbench/contrib/interactiveEditor/browser/interactiveEditorSession';
@@ -36,23 +38,23 @@ import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/se
3638
import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon';
3739

3840
const enum State {
39-
CREATE_SESSION,
40-
INIT_UI,
41-
WAIT_FOR_INPUT,
42-
MAKE_REQUEST,
43-
APPLY_RESPONSE,
44-
SHOW_RESPONSE,
45-
PAUSE,
46-
DONE,
41+
CREATE_SESSION = 'CREATE_SESSION',
42+
INIT_UI = 'INIT_UI',
43+
WAIT_FOR_INPUT = 'WAIT_FOR_INPUT',
44+
MAKE_REQUEST = 'MAKE_REQUEST',
45+
APPLY_RESPONSE = 'APPLY_RESPONSE',
46+
SHOW_RESPONSE = 'SHOW_RESPONSE',
47+
PAUSE = 'PAUSE',
48+
DONE = 'DONE',
4749
}
4850

4951
const enum Message {
5052
NONE = 0,
51-
END_SESSION = 2 ** 0,
52-
PAUSE_SESSION = 2 ** 1,
53-
CANCEL_REQUEST = 2 ** 2,
54-
CANCEL_INPUT = 2 ** 3,
55-
ACCEPT_INPUT = 2 ** 4
53+
END_SESSION = 1 << 0,
54+
PAUSE_SESSION = 1 << 1,
55+
CANCEL_REQUEST = 1 << 2,
56+
CANCEL_INPUT = 1 << 3,
57+
ACCEPT_INPUT = 1 << 4
5658
}
5759

5860
export interface InteractiveEditorRunOptions {
@@ -101,6 +103,7 @@ export class InteractiveEditorController implements IEditorContribution {
101103
@IConfigurationService private readonly _configurationService: IConfigurationService,
102104
@IModelService private readonly _modelService: IModelService,
103105
@INotebookEditorService private readonly _notebookEditorService: INotebookEditorService,
106+
@IDialogService private readonly _dialogService: IDialogService,
104107
@IContextKeyService contextKeyService: IContextKeyService,
105108
) {
106109
this._ctxHasActiveRequest = CTX_INTERACTIVE_EDITOR_HAS_ACTIVE_REQUEST.bindTo(contextKeyService);
@@ -148,39 +151,13 @@ export class InteractiveEditorController implements IEditorContribution {
148151

149152
private async _nextState(state: State, options: InteractiveEditorRunOptions | undefined): Promise<void> {
150153
this._logService.trace('[IE] setState to ', state);
151-
let nextState: State | undefined;
152-
switch (state) {
153-
case State.CREATE_SESSION:
154-
nextState = await this._createSession(options);
155-
break;
156-
case State.INIT_UI:
157-
nextState = await this._initUI();
158-
break;
159-
case State.WAIT_FOR_INPUT:
160-
nextState = await this._waitForInput(options);
161-
break;
162-
case State.MAKE_REQUEST:
163-
nextState = await this._makeRequest();
164-
break;
165-
case State.APPLY_RESPONSE:
166-
nextState = await this._applyResponse();
167-
break;
168-
case State.SHOW_RESPONSE:
169-
nextState = await this._showResponse();
170-
break;
171-
case State.PAUSE:
172-
this._pause();
173-
break;
174-
case State.DONE:
175-
this._done();
176-
break;
177-
}
154+
const nextState = await this[state](options);
178155
if (nextState) {
179156
this._nextState(nextState, options);
180157
}
181158
}
182159

183-
private async _createSession(options: InteractiveEditorRunOptions | undefined): Promise<State.DONE | State.INIT_UI> {
160+
private async [State.CREATE_SESSION](options: InteractiveEditorRunOptions | undefined): Promise<State.DONE | State.INIT_UI> {
184161
assertType(this._editor.hasModel());
185162

186163
let session: Session | undefined = options?.existingSession;
@@ -206,6 +183,7 @@ export class InteractiveEditorController implements IEditorContribution {
206183
delete options?.existingSession;
207184

208185
if (!session) {
186+
this._dialogService.info(localize('create.fail', "Failed to start editor chat"), localize('create.fail.detail', "Please consult the error log and try again later."));
209187
return State.DONE;
210188
}
211189

@@ -225,7 +203,7 @@ export class InteractiveEditorController implements IEditorContribution {
225203
return State.INIT_UI;
226204
}
227205

228-
private async _initUI(): Promise<State.WAIT_FOR_INPUT | State.SHOW_RESPONSE> {
206+
private async [State.INIT_UI](): Promise<State.WAIT_FOR_INPUT | State.SHOW_RESPONSE> {
229207
assertType(this._activeSession);
230208

231209
// hide/cancel inline completions when invoking IE
@@ -296,7 +274,7 @@ export class InteractiveEditorController implements IEditorContribution {
296274
}
297275
}
298276

299-
private async _waitForInput(options: InteractiveEditorRunOptions | undefined): Promise<State.DONE | State.PAUSE | State.WAIT_FOR_INPUT | State.MAKE_REQUEST> {
277+
private async [State.WAIT_FOR_INPUT](options: InteractiveEditorRunOptions | undefined): Promise<State.DONE | State.PAUSE | State.WAIT_FOR_INPUT | State.MAKE_REQUEST> {
300278
assertType(this._activeSession);
301279

302280
this._zone.show(this._activeSession.wholeRange.getEndPosition());
@@ -360,7 +338,7 @@ export class InteractiveEditorController implements IEditorContribution {
360338
return State.MAKE_REQUEST;
361339
}
362340

363-
private async _makeRequest(): Promise<State.APPLY_RESPONSE | State.PAUSE | State.DONE> {
341+
private async [State.MAKE_REQUEST](): Promise<State.APPLY_RESPONSE | State.PAUSE | State.DONE> {
364342
assertType(this._editor.hasModel());
365343
assertType(this._activeSession);
366344
assertType(this._activeSession.lastInput);
@@ -427,7 +405,7 @@ export class InteractiveEditorController implements IEditorContribution {
427405
}
428406
}
429407

430-
private async _applyResponse(): Promise<State.SHOW_RESPONSE | State.DONE> {
408+
private async [State.APPLY_RESPONSE](): Promise<State.SHOW_RESPONSE | State.DONE> {
431409
assertType(this._activeSession);
432410
assertType(this._strategy);
433411

@@ -461,7 +439,7 @@ export class InteractiveEditorController implements IEditorContribution {
461439
return State.SHOW_RESPONSE;
462440
}
463441

464-
private async _showResponse(): Promise<State.WAIT_FOR_INPUT | State.DONE> {
442+
private async [State.SHOW_RESPONSE](): Promise<State.WAIT_FOR_INPUT | State.DONE> {
465443
assertType(this._activeSession);
466444
assertType(this._strategy);
467445

@@ -510,7 +488,7 @@ export class InteractiveEditorController implements IEditorContribution {
510488
return State.WAIT_FOR_INPUT;
511489
}
512490

513-
private async _pause() {
491+
private async [State.PAUSE]() {
514492
assertType(this._activeSession);
515493

516494
this._ctxLastEditKind.reset();
@@ -528,10 +506,10 @@ export class InteractiveEditorController implements IEditorContribution {
528506
this._activeSession = undefined;
529507
}
530508

531-
private async _done() {
509+
private async [State.DONE]() {
532510
assertType(this._activeSession);
533511
this._interactiveEditorSessionService.releaseSession(this._activeSession);
534-
this._pause();
512+
this[State.PAUSE]();
535513
}
536514

537515
// ---- controller API
@@ -612,7 +590,13 @@ export class InteractiveEditorController implements IEditorContribution {
612590
if (this._strategy) {
613591
const strategy = this._strategy;
614592
this._strategy = undefined;
615-
await strategy?.apply();
593+
try {
594+
await strategy?.apply();
595+
} catch (err) {
596+
this._dialogService.error(localize('err.apply', "Failed to apply changes.", toErrorMessage(err)));
597+
this._logService.error('[IE] FAILED to apply changes');
598+
this._logService.error(err);
599+
}
616600
strategy?.dispose();
617601
this._messages.fire(Message.END_SESSION);
618602

@@ -626,7 +610,13 @@ export class InteractiveEditorController implements IEditorContribution {
626610
if (this._strategy) {
627611
const strategy = this._strategy;
628612
this._strategy = undefined;
629-
await strategy?.cancel();
613+
try {
614+
await strategy?.cancel();
615+
} catch (err) {
616+
this._dialogService.error(localize('err.discard', "Failed to discard changes.", toErrorMessage(err)));
617+
this._logService.error('[IE] FAILED to discard changes');
618+
this._logService.error(err);
619+
}
630620
strategy?.dispose();
631621
this._messages.fire(Message.END_SESSION);
632622
}

src/vs/workbench/contrib/interactiveEditor/browser/interactiveEditorLivePreviewWidget.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ export class InteractiveEditorLivePreviewWidget extends ZoneWidget {
148148
private _updateFromChanges(range: Range, changes: LineRangeMapping[]): void {
149149
assertType(this.editor.hasModel());
150150

151-
if (changes.length === 0) {
152-
// no change
151+
if (changes.length === 0 || this._textModelv0.getValueLength() === 0) {
152+
// no change or changes to an empty file
153153
this._logService.debug('[IE] livePreview-mode: no diff');
154154
this.hide();
155155

src/vs/workbench/contrib/interactiveEditor/browser/interactiveEditorSession.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,14 @@ export class InteractiveEditorSessionService implements IInteractiveEditorSessio
282282

283283
const textModel = editor.getModel();
284284
const selection = editor.getSelection();
285-
const raw = await provider.prepareInteractiveEditorSession(textModel, selection, token);
285+
let raw: IInteractiveEditorSession | undefined | null;
286+
try {
287+
raw = await provider.prepareInteractiveEditorSession(textModel, selection, token);
288+
} catch (error) {
289+
this._logService.error('[IE] FAILED to prepare session', provider.debugName);
290+
this._logService.error(error);
291+
return undefined;
292+
}
286293
if (!raw) {
287294
this._logService.trace('[IE] NO session', provider.debugName);
288295
return undefined;

src/vs/workbench/contrib/interactiveEditor/browser/interactiveEditorStrategies.ts

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ export abstract class EditModeStrategy {
4040

4141
abstract renderChanges(response: EditResponse, changes: LineRangeMapping[]): Promise<void>;
4242

43-
abstract hide(): Promise<void>;
44-
4543
abstract toggleInlineDiff(): void;
4644
}
4745

@@ -101,10 +99,6 @@ export class PreviewStrategy extends EditModeStrategy {
10199
}
102100
}
103101

104-
override async hide(): Promise<void> {
105-
// nothing to do, input widget will be hidden by controller
106-
}
107-
108102
async cancel(): Promise<void> {
109103
// nothing to do
110104
}
@@ -206,6 +200,7 @@ export class LiveStrategy extends EditModeStrategy {
206200
private readonly _inlineDiffDecorations: InlineDiffDecorations;
207201
private readonly _ctxInlineDiff: IContextKey<boolean>;
208202
private _lastResponse?: EditResponse;
203+
private _editCount: number = 0;
209204

210205
constructor(
211206
protected readonly _session: Session,
@@ -255,15 +250,14 @@ export class LiveStrategy extends EditModeStrategy {
255250
}
256251

257252
async apply() {
253+
if (this._editCount > 0) {
254+
this._editor.pushUndoStop();
255+
}
258256
if (this._lastResponse?.workspaceEdits) {
259257
await this._bulkEditService.apply(this._lastResponse.workspaceEdits);
260258
}
261259
}
262260

263-
override async hide(): Promise<void> {
264-
this._inlineDiffDecorations.clear();
265-
}
266-
267261
async cancel() {
268262
const { textModelN: modelN, textModel0: model0, lastSnapshot } = this._session;
269263
if (modelN.isDisposed() || (model0.isDisposed() && !lastSnapshot)) {
@@ -281,7 +275,7 @@ export class LiveStrategy extends EditModeStrategy {
281275
}
282276
}
283277

284-
override async makeChanges(_response: EditResponse, edits: ISingleEditOperation[]): Promise<void> {
278+
override async makeChanges(_response: EditResponse, edits: ISingleEditOperation[], ignoreInlineDiff?: boolean): Promise<void> {
285279
const cursorStateComputerAndInlineDiffCollection: ICursorStateComputer = (undoEdits) => {
286280
let last: Position | null = null;
287281
for (const edit of undoEdits) {
@@ -291,9 +285,11 @@ export class LiveStrategy extends EditModeStrategy {
291285
return last && [Selection.fromPositions(last)];
292286
};
293287

294-
this._editor.pushUndoStop();
295-
this._editor.executeEdits('interactive-editor-live', edits, cursorStateComputerAndInlineDiffCollection);
296-
this._editor.pushUndoStop();
288+
// push undo stop before first edit
289+
if (++this._editCount === 1) {
290+
this._editor.pushUndoStop();
291+
}
292+
this._editor.executeEdits('interactive-editor-live', edits, ignoreInlineDiff ? undefined : cursorStateComputerAndInlineDiffCollection);
297293
}
298294

299295
override async renderChanges(response: EditResponse, textModel0Changes: LineRangeMapping[]) {
@@ -356,15 +352,8 @@ export class LivePreviewStrategy extends LiveStrategy {
356352
super.dispose();
357353
}
358354

359-
override async hide(): Promise<void> {
360-
this._diffZone.hide();
361-
super.hide();
362-
}
363-
364355
override async makeChanges(_response: EditResponse, edits: ISingleEditOperation[]): Promise<void> {
365-
this._editor.pushUndoStop();
366-
this._editor.executeEdits('interactive-editor-livePreview', edits);
367-
this._editor.pushUndoStop();
356+
super.makeChanges(_response, edits, true);
368357
}
369358

370359
override async renderChanges(response: EditResponse, changes: LineRangeMapping[]) {

0 commit comments

Comments
 (0)