extract some parts into preview and live strategies

This commit is contained in:
Johannes 2023-04-14 10:08:23 +02:00
parent 9567c0cea0
commit bb08d3697e
No known key found for this signature in database
GPG key ID: 6DEF802A22264FCA
3 changed files with 123 additions and 47 deletions

View file

@ -124,25 +124,6 @@ export class StopRequestAction extends AbstractInteractiveEditorAction {
}
}
export class CancelSessionAction extends AbstractInteractiveEditorAction {
constructor() {
super({
id: 'interactiveEditor.cancel',
title: localize('cancel', 'Cancel'),
precondition: CTX_INTERACTIVE_EDITOR_VISIBLE,
keybinding: {
weight: KeybindingWeight.EditorContrib - 1,
primary: KeyCode.Escape
}
});
}
runInteractiveEditorCommand(_accessor: ServicesAccessor, ctrl: InteractiveEditorController, _editor: ICodeEditor, ..._args: any[]): void {
ctrl.cancelSession();
}
}
export class ArrowOutUpAction extends AbstractInteractiveEditorAction {
constructor() {
super({
@ -380,7 +361,7 @@ export class ToggleInlineDiff extends AbstractInteractiveEditorAction {
id: MENU_INTERACTIVE_EDITOR_WIDGET_STATUS,
when: CTX_INTERACTIVE_EDITOR_EDIT_MODE.isEqualTo('direct'),
group: '0_main',
order: 1
order: 10
}
});
}
@ -397,10 +378,14 @@ export class ApplyPreviewEdits extends AbstractInteractiveEditorAction {
id: 'interactiveEditor.applyEdits',
title: localize('applyEdits', 'Apply Changes'),
icon: Codicon.check,
precondition: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_VISIBLE, CTX_INTERACTIVE_EDITOR_EDIT_MODE.isEqualTo('preview')),
precondition: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_VISIBLE),
keybinding: {
weight: KeybindingWeight.EditorContrib + 10,
primary: KeyMod.CtrlCmd | KeyCode.Enter
},
menu: {
id: MENU_INTERACTIVE_EDITOR_WIDGET_STATUS,
when: CTX_INTERACTIVE_EDITOR_EDIT_MODE.isEqualTo('preview'),
// when: CTX_INTERACTIVE_EDITOR_EDIT_MODE.isEqualTo('preview'),
group: '0_main',
order: 0
}
@ -415,7 +400,6 @@ export class ApplyPreviewEdits extends AbstractInteractiveEditorAction {
logService.warn('FAILED to apply changes, no edit response');
return;
}
ctrl.cancelSession();
if (edit.singleCreateFileEdit) {
editorService.openEditor({ resource: edit.singleCreateFileEdit.uri }, SIDE_GROUP);
}
@ -423,6 +407,32 @@ export class ApplyPreviewEdits extends AbstractInteractiveEditorAction {
}
}
export class CancelSessionAction extends AbstractInteractiveEditorAction {
constructor() {
super({
id: 'interactiveEditor.cancel',
title: localize('cancel', 'Cancel'),
icon: Codicon.clearAll,
precondition: CTX_INTERACTIVE_EDITOR_VISIBLE,
keybinding: {
weight: KeybindingWeight.EditorContrib - 1,
primary: KeyCode.Escape
},
menu: {
id: MENU_INTERACTIVE_EDITOR_WIDGET_STATUS,
// when: CTX_INTERACTIVE_EDITOR_EDIT_MODE.isEqualTo('preview'),
group: '0_main',
order: 1
}
});
}
runInteractiveEditorCommand(_accessor: ServicesAccessor, ctrl: InteractiveEditorController, _editor: ICodeEditor, ..._args: any[]): void {
ctrl.cancelSession();
}
}
export class CopyRecordings extends AbstractInteractiveEditorAction {
constructor() {

View file

@ -225,7 +225,7 @@ class LastEditorState {
) { }
}
type EditMode = 'preview' | 'livePreview' | 'direct';
type EditMode = 'live' | 'livePreview' | 'preview';
export class InteractiveEditorController implements IEditorContribution {
@ -261,6 +261,7 @@ export class InteractiveEditorController implements IEditorContribution {
private readonly _ctxLastFeedbackKind: IContextKey<'helpful' | 'unhelpful' | ''>;
private _lastEditState?: LastEditorState;
private _strategy?: LiveStrategy | PreviewStrategy;
private _lastInlineDecorations?: InlineDiffDecorations;
private _inlineDiffEnabled: boolean = false;
@ -271,7 +272,6 @@ export class InteractiveEditorController implements IEditorContribution {
private readonly _editor: ICodeEditor,
@IInstantiationService private readonly _instaService: IInstantiationService,
@IInteractiveEditorService private readonly _interactiveEditorService: IInteractiveEditorService,
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
@IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService,
@ILogService private readonly _logService: ILogService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ -299,9 +299,13 @@ export class InteractiveEditorController implements IEditorContribution {
return InteractiveEditorController.ID;
}
private _getMode(): EditMode {
return this._configurationService.getValue('interactiveEditor.editMode');
}
async run(initialRange?: Range): Promise<void> {
const editMode: EditMode = this._configurationService.getValue('interactiveEditor.editMode');
const editMode = this._getMode();
this._ctsSession.dispose(true);
@ -336,6 +340,8 @@ export class InteractiveEditorController implements IEditorContribution {
undos: ''
};
this._strategy = this._instaService.createInstance(editMode === 'preview' ? PreviewStrategy : LiveStrategy, textModel, textModel.getAlternativeVersionId());
this._inlineDiffEnabled = this._storageService.getBoolean(InteractiveEditorController._inlineDiffStorageKey, StorageScope.PROFILE, false);
const inlineDiffDecorations = new InlineDiffDecorations(this._editor, this._inlineDiffEnabled);
this._lastInlineDecorations = inlineDiffDecorations;
@ -525,9 +531,8 @@ export class InteractiveEditorController implements IEditorContribution {
const editResponse = new EditResponse(textModel.uri, reply);
if (editResponse.workspaceEdits && (!editResponse.singleCreateFileEdit || editMode === 'direct')) {
this._bulkEditService.apply(editResponse.workspaceEdits, { editor: this._editor, label: localize('ie', "{0}", input), showPreview: true });
// todo@jrieken keep interactive editor?
const canContinue = this._strategy.update(editResponse);
if (!canContinue) {
break;
}
@ -583,7 +588,9 @@ export class InteractiveEditorController implements IEditorContribution {
ignoreModelChanges = false;
}
inlineDiffDecorations.update();
if (editMode === 'live') {
inlineDiffDecorations.update();
}
// line count
const lineSet = new Set<number>();
@ -664,10 +671,6 @@ export class InteractiveEditorController implements IEditorContribution {
this._ctsRequest?.cancel();
}
cancelSession() {
this._ctsSession.cancel();
}
arrowOut(up: boolean): void {
if (this._zone.position && this._editor.hasModel()) {
const { column } = this._editor.getPosition();
@ -730,22 +733,87 @@ export class InteractiveEditorController implements IEditorContribution {
if (!this._lastEditState) {
return undefined;
}
const { response } = this._lastEditState;
await this._strategy?.apply();
this._ctsSession.cancel();
return response;
}
const { model, modelVersionId, response } = this._lastEditState;
cancelSession() {
this._ctsSession.cancel();
this._strategy?.cancel();
}
}
class PreviewStrategy {
private _lastResponse?: EditResponse;
constructor(
private readonly _model: ITextModel,
private readonly _versionId: number,
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
) { }
update(response: EditResponse): boolean {
this._lastResponse = response;
if (!response.workspaceEdits || response.singleCreateFileEdit) {
// preview stategy can handle simple workspace edit (single file create)
return true;
}
this._bulkEditService.apply(response.workspaceEdits, { showPreview: true });
return false;
}
async apply() {
const response = this._lastResponse;
if (!response) {
return;
}
if (response.workspaceEdits) {
await this._bulkEditService.apply(response.workspaceEdits);
} else if (!response.workspaceEditsIncludeLocalEdits) {
if (model.getAlternativeVersionId() === modelVersionId) {
model.pushStackElement();
if (this._model.getAlternativeVersionId() === this._versionId) {
this._model.pushStackElement();
const edits = response.localEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text));
model.pushEditOperations(null, edits, () => null);
model.pushStackElement();
this._model.pushEditOperations(null, edits, () => null);
this._model.pushStackElement();
}
}
}
return response;
cancel(): void {
// nothing to do
}
}
class LiveStrategy {
constructor(
private readonly _model: ITextModel,
private readonly _versionId: number,
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
) { }
update(response: EditResponse): boolean {
if (response.workspaceEdits) {
this._bulkEditService.apply(response.workspaceEdits, { showPreview: true });
return false;
}
return true;
}
async apply() {
// nothing to do
}
cancel(): void {
while (this._model.getAlternativeVersionId() !== this._versionId) {
this._model.undo();
}
}
}

View file

@ -14,7 +14,7 @@ import { ITextModel } from 'vs/editor/common/model';
import { localize } from 'vs/nls';
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Registry } from 'vs/platform/registry/common/platform';
import { editorHoverHighlight, editorWidgetBorder, focusBorder, inputBackground, inputPlaceholderForeground, registerColor, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
@ -105,9 +105,7 @@ export const CTX_INTERACTIVE_EDITOR_HAS_RESPONSE = new RawContextKey<boolean>('i
export const CTX_INTERACTIVE_EDITOR_INLNE_DIFF = new RawContextKey<boolean>('interactiveEditorInlineDiff', false, localize('interactiveEditorInlineDiff', "Whether interactive editor show inline diffs for changes"));
export const CTX_INTERACTIVE_EDITOR_LAST_EDIT_TYPE = new RawContextKey<'simple' | ''>('interactiveEditorLastEditKind', '', localize('interactiveEditorLastEditKind', "The last kind of edit that was performed"));
export const CTX_INTERACTIVE_EDITOR_LAST_FEEDBACK = new RawContextKey<'unhelpful' | 'helpful' | ''>('interactiveEditorLastFeedbackKind', '', localize('interactiveEditorLastFeedbackKind', "The last kind of feedback that was provided"));
export const CTX_VALUE_INTERACTIVE_EDITOR_EDIT_MODE_YOLO = ContextKeyExpr.equals('config.interactiveEditor.editMode', 'direct');
export const CTX_INTERACTIVE_EDITOR_EDIT_MODE = new RawContextKey<'direct' | 'preview'>('config.interactiveEditor.editMode', 'direct');
export const CTX_INTERACTIVE_EDITOR_EDIT_MODE = new RawContextKey<'live' | 'livePreview' | 'preview'>('config.interactiveEditor.editMode', 'live');
// --- menus
@ -119,7 +117,7 @@ MenuRegistry.appendMenuItem(MENU_INTERACTIVE_EDITOR_WIDGET_STATUS, {
title: localize('undo', "Undo..."),
icon: Codicon.discard,
group: '0_main',
order: 0,
order: 2,
when: CTX_INTERACTIVE_EDITOR_EDIT_MODE.isEqualTo('direct')
});
@ -140,9 +138,9 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
properties: {
'interactiveEditor.editMode': {
description: localize('editMode', "Configure if changes crafted in the interactive editor are applied directly or previewed first"),
default: 'direct',
default: 'live',
type: 'string',
enum: ['preview', 'livePreview', 'direct']
enum: ['live', 'livePreview', 'preview']
}
}
});