Skip to content

Commit

Permalink
joh/remarkable egret (#182507)
Browse files Browse the repository at this point in the history
* For preview modes bring back actions to discard but keep changes in clipboard or new file, in live modes ESC accepts, in preview is cancels, DropDownWithDefault tweaks so that the first option can always be the default

* handle reentrant editor chat with cancel or accept (depending on mode)
  • Loading branch information
jrieken committed May 15, 2023
1 parent e14e406 commit 9e5ddd4
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 120 deletions.
21 changes: 13 additions & 8 deletions src/vs/platform/actions/browser/menuEntryActionViewItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,14 +320,15 @@ export class SubmenuEntryActionViewItem extends DropdownMenuActionViewItem {

export interface IDropdownWithDefaultActionViewItemOptions extends IDropdownMenuActionViewItemOptions {
renderKeybindingWithDefaultActionLabel?: boolean;
persistLastActionId?: boolean;
}

export class DropdownWithDefaultActionViewItem extends BaseActionViewItem {
private readonly _options: IDropdownWithDefaultActionViewItemOptions | undefined;
private _defaultAction: ActionViewItem;
private _dropdown: DropdownMenuActionViewItem;
private readonly _dropdown: DropdownMenuActionViewItem;
private _container: HTMLElement | null = null;
private _storageKey: string;
private readonly _storageKey: string;

get onDidChangeDropdownVisibility(): Event<boolean> {
return this._dropdown.onDidChangeVisibility;
Expand All @@ -349,7 +350,7 @@ export class DropdownWithDefaultActionViewItem extends BaseActionViewItem {

// determine default action
let defaultAction: IAction | undefined;
const defaultActionId = _storageService.get(this._storageKey, StorageScope.WORKSPACE);
const defaultActionId = options?.persistLastActionId ? _storageService.get(this._storageKey, StorageScope.WORKSPACE) : undefined;
if (defaultActionId) {
defaultAction = submenuAction.actions.find(a => defaultActionId === a.id);
}
Expand All @@ -359,11 +360,13 @@ export class DropdownWithDefaultActionViewItem extends BaseActionViewItem {

this._defaultAction = this._instaService.createInstance(MenuEntryActionViewItem, <MenuItemAction>defaultAction, { keybinding: this._getDefaultActionKeybindingLabel(defaultAction) });

const dropdownOptions = Object.assign({}, options ?? Object.create(null), {
const dropdownOptions: IDropdownMenuActionViewItemOptions = {
keybindingProvider: action => this._keybindingService.lookupKeybinding(action.id),
...options,
menuAsChild: options?.menuAsChild ?? true,
classNames: options?.classNames ?? ['codicon', 'codicon-chevron-down'],
actionRunner: options?.actionRunner ?? new ActionRunner()
});
actionRunner: options?.actionRunner ?? new ActionRunner(),
};

this._dropdown = new DropdownMenuActionViewItem(submenuAction, submenuAction.actions, this._contextMenuService, dropdownOptions);
this._dropdown.actionRunner.onDidRun((e: IRunEvent) => {
Expand All @@ -374,7 +377,9 @@ export class DropdownWithDefaultActionViewItem extends BaseActionViewItem {
}

private update(lastAction: MenuItemAction): void {
this._storageService.store(this._storageKey, lastAction.id, StorageScope.WORKSPACE, StorageTarget.MACHINE);
if (this._options?.persistLastActionId) {
this._storageService.store(this._storageKey, lastAction.id, StorageScope.WORKSPACE, StorageTarget.MACHINE);
}

this._defaultAction.dispose();
this._defaultAction = this._instaService.createInstance(MenuEntryActionViewItem, lastAction, { keybinding: this._getDefaultActionKeybindingLabel(lastAction) });
Expand Down Expand Up @@ -505,7 +510,7 @@ export function createActionViewItem(instaService: IInstantiationService, action
return instaService.createInstance(SubmenuEntrySelectActionViewItem, action);
} else {
if (action.item.rememberDefaultAction) {
return instaService.createInstance(DropdownWithDefaultActionViewItem, action, options);
return instaService.createInstance(DropdownWithDefaultActionViewItem, action, { ...options, persistLastActionId: true });
} else {
return instaService.createInstance(SubmenuEntryActionViewItem, action, options);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ registerEditorContribution(INTERACTIVE_EDITOR_ID, InteractiveEditorController, E
registerAction2(interactiveEditorActions.StartSessionAction);
registerAction2(interactiveEditorActions.MakeRequestAction);
registerAction2(interactiveEditorActions.StopRequestAction);
registerAction2(interactiveEditorActions.DicardAction);
registerAction2(interactiveEditorActions.DiscardToClipboardAction);
registerAction2(interactiveEditorActions.DiscardUndoToNewFileAction);
registerAction2(interactiveEditorActions.CancelSessionAction);

registerAction2(interactiveEditorActions.ArrowOutUpAction);
registerAction2(interactiveEditorActions.ArrowOutDownAction);
registerAction2(interactiveEditorActions.FocusInteractiveEditor);
Expand All @@ -30,9 +34,6 @@ registerAction2(interactiveEditorActions.ViewInChatAction);
registerAction2(interactiveEditorActions.ExpandMessageAction);
registerAction2(interactiveEditorActions.ContractMessageAction);

registerAction2(interactiveEditorActions.UndoToClipboard);
registerAction2(interactiveEditorActions.UndoToNewFile);
registerAction2(interactiveEditorActions.UndoCommand);
registerAction2(interactiveEditorActions.ToggleInlineDiff);
registerAction2(interactiveEditorActions.FeebackHelpfulCommand);
registerAction2(interactiveEditorActions.FeebackUnhelpfulCommand);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { EditorAction2 } from 'vs/editor/browser/editorExtensions';
import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { InteractiveEditorController, InteractiveEditorRunOptions } from 'vs/workbench/contrib/interactiveEditor/browser/interactiveEditorController';
import { CTX_INTERACTIVE_EDITOR_FOCUSED, CTX_INTERACTIVE_EDITOR_HAS_ACTIVE_REQUEST, CTX_INTERACTIVE_EDITOR_HAS_PROVIDER, CTX_INTERACTIVE_EDITOR_INNER_CURSOR_FIRST, CTX_INTERACTIVE_EDITOR_INNER_CURSOR_LAST, CTX_INTERACTIVE_EDITOR_EMPTY, CTX_INTERACTIVE_EDITOR_OUTER_CURSOR_POSITION, CTX_INTERACTIVE_EDITOR_VISIBLE, MENU_INTERACTIVE_EDITOR_WIDGET, CTX_INTERACTIVE_EDITOR_LAST_EDIT_TYPE, MENU_INTERACTIVE_EDITOR_WIDGET_UNDO, MENU_INTERACTIVE_EDITOR_WIDGET_STATUS, CTX_INTERACTIVE_EDITOR_LAST_FEEDBACK, CTX_INTERACTIVE_EDITOR_INLNE_DIFF, CTX_INTERACTIVE_EDITOR_EDIT_MODE, EditMode, CTX_INTERACTIVE_EDITOR_LAST_RESPONSE_TYPE, MENU_INTERACTIVE_EDITOR_WIDGET_MARKDOWN_MESSAGE, CTX_INTERACTIVE_EDITOR_MESSAGE_CROP_STATE, CTX_INTERACTIVE_EDITOR_DOCUMENT_CHANGED } from 'vs/workbench/contrib/interactiveEditor/common/interactiveEditor';
import { CTX_INTERACTIVE_EDITOR_FOCUSED, CTX_INTERACTIVE_EDITOR_HAS_ACTIVE_REQUEST, CTX_INTERACTIVE_EDITOR_HAS_PROVIDER, CTX_INTERACTIVE_EDITOR_INNER_CURSOR_FIRST, CTX_INTERACTIVE_EDITOR_INNER_CURSOR_LAST, CTX_INTERACTIVE_EDITOR_EMPTY, CTX_INTERACTIVE_EDITOR_OUTER_CURSOR_POSITION, CTX_INTERACTIVE_EDITOR_VISIBLE, MENU_INTERACTIVE_EDITOR_WIDGET, MENU_INTERACTIVE_EDITOR_WIDGET_DISCARD, MENU_INTERACTIVE_EDITOR_WIDGET_STATUS, CTX_INTERACTIVE_EDITOR_LAST_FEEDBACK, CTX_INTERACTIVE_EDITOR_INLNE_DIFF, CTX_INTERACTIVE_EDITOR_EDIT_MODE, EditMode, CTX_INTERACTIVE_EDITOR_LAST_RESPONSE_TYPE, MENU_INTERACTIVE_EDITOR_WIDGET_MARKDOWN_MESSAGE, CTX_INTERACTIVE_EDITOR_MESSAGE_CROP_STATE, CTX_INTERACTIVE_EDITOR_DOCUMENT_CHANGED, CTX_INTERACTIVE_EDITOR_DID_EDIT } from 'vs/workbench/contrib/interactiveEditor/common/interactiveEditor';
import { localize } from 'vs/nls';
import { IAction2Options } from 'vs/platform/actions/common/actions';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
Expand All @@ -20,7 +20,6 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { IUntitledTextResourceEditorInput } from 'vs/workbench/common/editor';
import { ILogService } from 'vs/platform/log/common/log';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { Range } from 'vs/editor/common/core/range';
import { fromNow } from 'vs/base/common/date';
Expand Down Expand Up @@ -251,86 +250,79 @@ export class NextFromHistory extends AbstractInteractiveEditorAction {
}
}


export class UndoToClipboard extends AbstractInteractiveEditorAction {
export class DicardAction extends AbstractInteractiveEditorAction {

constructor() {
super({
id: 'interactiveEditor.undoToClipboard',
title: localize('undo.clipboard', 'Undo to Clipboard'),
precondition: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_VISIBLE, CTX_INTERACTIVE_EDITOR_LAST_EDIT_TYPE.isEqualTo('simple')),
keybinding: {
weight: KeybindingWeight.EditorContrib + 10,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyZ,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KeyZ },
},
id: 'interactiveEditor.discard',
title: localize('discard', 'Discard'),
icon: Codicon.discard,
precondition: CTX_INTERACTIVE_EDITOR_VISIBLE,
// keybinding: {
// weight: KeybindingWeight.EditorContrib - 1,
// primary: KeyCode.Escape
// },
menu: {
when: CTX_INTERACTIVE_EDITOR_LAST_EDIT_TYPE.isEqualTo('simple'),
id: MENU_INTERACTIVE_EDITOR_WIDGET_UNDO,
group: '1_undo',
order: 1
id: MENU_INTERACTIVE_EDITOR_WIDGET_DISCARD,
order: 0
}
});
}

override runInteractiveEditorCommand(accessor: ServicesAccessor, ctrl: InteractiveEditorController): void {
const clipboardService = accessor.get(IClipboardService);
const lastText = ctrl.undoLast();
if (lastText !== undefined) {
clipboardService.writeText(lastText);
}
async runInteractiveEditorCommand(_accessor: ServicesAccessor, ctrl: InteractiveEditorController, _editor: ICodeEditor, ..._args: any[]): Promise<void> {
await ctrl.cancelSession();
}
}

export class UndoToNewFile extends AbstractInteractiveEditorAction {
export class DiscardToClipboardAction extends AbstractInteractiveEditorAction {

constructor() {
super({
id: 'interactiveEditor.undoToFile',
title: localize('undo.newfile', 'Undo to New File'),
precondition: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_VISIBLE, CTX_INTERACTIVE_EDITOR_LAST_EDIT_TYPE.isEqualTo('simple')),
id: 'interactiveEditor.discardToClipboard',
title: localize('undo.clipboard', 'Discard to Clipboard'),
precondition: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_VISIBLE, CTX_INTERACTIVE_EDITOR_DID_EDIT),
// keybinding: {
// weight: KeybindingWeight.EditorContrib + 10,
// primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyZ,
// mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KeyZ },
// },
menu: {
when: CTX_INTERACTIVE_EDITOR_LAST_EDIT_TYPE.isEqualTo('simple'),
id: MENU_INTERACTIVE_EDITOR_WIDGET_UNDO,
group: '1_undo',
order: 2
id: MENU_INTERACTIVE_EDITOR_WIDGET_DISCARD,
order: 1
}
});
}

override runInteractiveEditorCommand(accessor: ServicesAccessor, ctrl: InteractiveEditorController, editor: ICodeEditor, ..._args: any[]): void {
const editorService = accessor.get(IEditorService);
const lastText = ctrl.undoLast();
if (lastText !== undefined) {
const input: IUntitledTextResourceEditorInput = { forceUntitled: true, resource: undefined, contents: lastText, languageId: editor.getModel()?.getLanguageId() };
editorService.openEditor(input, SIDE_GROUP);
override async runInteractiveEditorCommand(accessor: ServicesAccessor, ctrl: InteractiveEditorController): Promise<void> {
const clipboardService = accessor.get(IClipboardService);
const changedText = await ctrl.cancelSession();
if (changedText !== undefined) {
clipboardService.writeText(changedText);
}
}
}

export class UndoCommand extends AbstractInteractiveEditorAction {
export class DiscardUndoToNewFileAction extends AbstractInteractiveEditorAction {

constructor() {
super({
id: 'interactiveEditor.undo',
title: localize('undo', 'Undo'),
icon: Codicon.commentDiscussion,
precondition: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_VISIBLE, CTX_INTERACTIVE_EDITOR_LAST_EDIT_TYPE.isEqualTo('simple')),
// keybinding: {
// weight: KeybindingWeight.EditorContrib + 10,
// primary: KeyMod.CtrlCmd | KeyCode.KeyZ,
// },
id: 'interactiveEditor.discardToFile',
title: localize('undo.newfile', 'Discard to New File'),
precondition: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_VISIBLE, CTX_INTERACTIVE_EDITOR_DID_EDIT),
menu: {
when: CTX_INTERACTIVE_EDITOR_LAST_EDIT_TYPE.isEqualTo('simple'),
id: MENU_INTERACTIVE_EDITOR_WIDGET_UNDO,
group: '1_undo',
order: 3
id: MENU_INTERACTIVE_EDITOR_WIDGET_DISCARD,
order: 2
}
});
}

override runInteractiveEditorCommand(_accessor: ServicesAccessor, ctrl: InteractiveEditorController): void {
ctrl.undoLast();
override async runInteractiveEditorCommand(accessor: ServicesAccessor, ctrl: InteractiveEditorController, editor: ICodeEditor, ..._args: any[]): Promise<void> {
const editorService = accessor.get(IEditorService);
const changedText = await ctrl.cancelSession();
if (changedText !== undefined) {
const input: IUntitledTextResourceEditorInput = { forceUntitled: true, resource: undefined, contents: changedText, languageId: editor.getModel()?.getLanguageId() };
editorService.openEditor(input, SIDE_GROUP);
}
}
}

Expand Down Expand Up @@ -409,30 +401,25 @@ export class ApplyPreviewEdits extends AbstractInteractiveEditorAction {
title: localize('applyEdits', 'Apply Changes'),
icon: Codicon.check,
precondition: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_VISIBLE, ContextKeyExpr.or(CTX_INTERACTIVE_EDITOR_DOCUMENT_CHANGED.toNegated(), CTX_INTERACTIVE_EDITOR_EDIT_MODE.notEqualsTo(EditMode.Preview))),
keybinding: {
keybinding: [{
weight: KeybindingWeight.EditorContrib + 10,
primary: KeyMod.CtrlCmd | KeyCode.Enter
},
primary: KeyMod.CtrlCmd | KeyCode.Enter,
}, {
weight: KeybindingWeight.EditorContrib + 10,
primary: KeyCode.Escape,
when: CTX_INTERACTIVE_EDITOR_EDIT_MODE.notEqualsTo(EditMode.Preview)
}],
menu: {
id: MENU_INTERACTIVE_EDITOR_WIDGET_STATUS,
when: CTX_INTERACTIVE_EDITOR_EDIT_MODE.isEqualTo(EditMode.Preview),
group: '0_main',
order: 0
}
});
}

override async runInteractiveEditorCommand(accessor: ServicesAccessor, ctrl: InteractiveEditorController): Promise<void> {
const logService = accessor.get(ILogService);
const editorService = accessor.get(IEditorService);
const edit = await ctrl.applyChanges();
if (!edit) {
logService.warn('FAILED to apply changes, no edit response');
return;
}
if (edit.singleCreateFileEdit) {
editorService.openEditor({ resource: edit.singleCreateFileEdit.uri }, SIDE_GROUP);
}

override async runInteractiveEditorCommand(_accessor: ServicesAccessor, ctrl: InteractiveEditorController): Promise<void> {
await ctrl.applyChanges();
}
}

Expand All @@ -441,15 +428,16 @@ export class CancelSessionAction extends AbstractInteractiveEditorAction {
constructor() {
super({
id: 'interactiveEditor.cancel',
title: localize('discard', 'Discard Changes'),
title: localize('cancel', 'Cancel'),
icon: Codicon.clearAll,
precondition: CTX_INTERACTIVE_EDITOR_VISIBLE,
precondition: ContextKeyExpr.and(CTX_INTERACTIVE_EDITOR_VISIBLE, CTX_INTERACTIVE_EDITOR_EDIT_MODE.isEqualTo(EditMode.Preview)),
keybinding: {
weight: KeybindingWeight.EditorContrib - 1,
primary: KeyCode.Escape
},
menu: {
id: MENU_INTERACTIVE_EDITOR_WIDGET_STATUS,
when: CTX_INTERACTIVE_EDITOR_EDIT_MODE.isEqualTo(EditMode.Preview),
group: '0_main',
order: 1
}
Expand All @@ -468,7 +456,8 @@ export class CopyRecordings extends AbstractInteractiveEditorAction {
id: 'interactiveEditor.copyRecordings',
f1: true,
title: {
value: localize('copyRecordings', '(Developer) Write Exchange to Clipboard'), original: '(Developer) Write Exchange to Clipboard'
value: localize('copyRecordings', '(Developer) Write Exchange to Clipboard'),
original: '(Developer) Write Exchange to Clipboard'
}
});
}
Expand Down

0 comments on commit 9e5ddd4

Please sign in to comment.