Path: blob/main/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts
3296 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*--------------------------------------------------------------------------------------------*/45import { Codicon } from '../../../../base/common/codicons.js';6import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';7import { ICodeEditor, isCodeEditor, isDiffEditor } from '../../../../editor/browser/editorBrowser.js';8import { EditorAction2 } from '../../../../editor/browser/editorExtensions.js';9import { EmbeddedDiffEditorWidget } from '../../../../editor/browser/widget/diffEditor/embeddedDiffEditorWidget.js';10import { EmbeddedCodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js';11import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js';12import { InlineChatController, InlineChatController1, InlineChatController2, InlineChatRunOptions } from './inlineChatController.js';13import { ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, InlineChatResponseType, ACTION_REGENERATE_RESPONSE, ACTION_VIEW_IN_CHAT, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, MENU_INLINE_CHAT_ZONE, ACTION_DISCARD_CHANGES, CTX_INLINE_CHAT_POSSIBLE, ACTION_START, CTX_INLINE_CHAT_HAS_AGENT2, MENU_INLINE_CHAT_SIDE } from '../common/inlineChat.js';14import { ctxHasEditorModification, ctxHasRequestInProgress, ctxIsGlobalEditingSession, ctxRequestCount } from '../../chat/browser/chatEditing/chatEditingEditorContextKeys.js';15import { localize, localize2 } from '../../../../nls.js';16import { Action2, IAction2Options, MenuId } from '../../../../platform/actions/common/actions.js';17import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';18import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';19import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';20import { IEditorService } from '../../../services/editor/common/editorService.js';21import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';22import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from '../../../../platform/accessibility/common/accessibility.js';23import { CommandsRegistry } from '../../../../platform/commands/common/commands.js';24import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js';25import { IPreferencesService } from '../../../services/preferences/common/preferences.js';26import { ILogService } from '../../../../platform/log/common/log.js';27import { IChatService } from '../../chat/common/chatService.js';28import { ChatContextKeys } from '../../chat/common/chatContextKeys.js';29import { HunkInformation } from './inlineChatSession.js';30import { IChatWidgetService } from '../../chat/browser/chat.js';31import { IInlineChatSessionService } from './inlineChatSessionService.js';323334CommandsRegistry.registerCommandAlias('interactiveEditor.start', 'inlineChat.start');35CommandsRegistry.registerCommandAlias('interactive.acceptChanges', ACTION_ACCEPT_CHANGES);363738export const START_INLINE_CHAT = registerIcon('start-inline-chat', Codicon.sparkle, localize('startInlineChat', 'Icon which spawns the inline chat from the editor toolbar.'));3940// some gymnastics to enable hold for speech without moving the StartSessionAction into the electron-layer4142export interface IHoldForSpeech {43(accessor: ServicesAccessor, controller: InlineChatController, source: Action2): void;44}45let _holdForSpeech: IHoldForSpeech | undefined = undefined;46export function setHoldForSpeech(holdForSpeech: IHoldForSpeech) {47_holdForSpeech = holdForSpeech;48}4950const inlineChatContextKey = ContextKeyExpr.and(51ContextKeyExpr.or(CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_HAS_AGENT2),52CTX_INLINE_CHAT_POSSIBLE,53EditorContextKeys.writable,54EditorContextKeys.editorSimpleInput.negate()55);5657export class StartSessionAction extends Action2 {5859constructor() {60super({61id: ACTION_START,62title: localize2('run', 'Open Inline Chat'),63category: AbstractInline1ChatAction.category,64f1: true,65precondition: inlineChatContextKey,66keybinding: {67when: EditorContextKeys.focus,68weight: KeybindingWeight.WorkbenchContrib,69primary: KeyMod.CtrlCmd | KeyCode.KeyI70},71icon: START_INLINE_CHAT,72menu: [{73id: MenuId.EditorContext,74group: '1_chat',75order: 3,76when: inlineChatContextKey77}, {78id: MenuId.ChatTitleBarMenu,79group: 'a_open',80order: 3,81}]82});83}84override run(accessor: ServicesAccessor, ...args: any[]): any {8586const codeEditorService = accessor.get(ICodeEditorService);87const editor = codeEditorService.getActiveCodeEditor();88if (!editor || editor.isSimpleWidget) {89// well, at least we tried...90return;91}929394// precondition does hold95return editor.invokeWithinContext((editorAccessor) => {96const kbService = editorAccessor.get(IContextKeyService);97const logService = editorAccessor.get(ILogService);98const enabled = kbService.contextMatchesRules(this.desc.precondition ?? undefined);99if (!enabled) {100logService.debug(`[EditorAction2] NOT running command because its precondition is FALSE`, this.desc.id, this.desc.precondition?.serialize());101return;102}103return this._runEditorCommand(editorAccessor, editor, ...args);104});105}106107private _runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) {108109const ctrl = InlineChatController.get(editor);110if (!ctrl) {111return;112}113114if (_holdForSpeech) {115accessor.get(IInstantiationService).invokeFunction(_holdForSpeech, ctrl, this);116}117118let options: InlineChatRunOptions | undefined;119const arg = _args[0];120if (arg && InlineChatRunOptions.isInlineChatRunOptions(arg)) {121options = arg;122}123InlineChatController.get(editor)?.run({ ...options });124}125}126127export class FocusInlineChat extends EditorAction2 {128129constructor() {130super({131id: 'inlineChat.focus',132title: localize2('focus', "Focus Input"),133f1: true,134category: AbstractInline1ChatAction.category,135precondition: ContextKeyExpr.and(EditorContextKeys.editorTextFocus, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_FOCUSED.negate(), CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()),136keybinding: [{137weight: KeybindingWeight.EditorCore + 10, // win against core_command138when: ContextKeyExpr.and(CTX_INLINE_CHAT_OUTER_CURSOR_POSITION.isEqualTo('above'), EditorContextKeys.isEmbeddedDiffEditor.negate()),139primary: KeyMod.CtrlCmd | KeyCode.DownArrow,140}, {141weight: KeybindingWeight.EditorCore + 10, // win against core_command142when: ContextKeyExpr.and(CTX_INLINE_CHAT_OUTER_CURSOR_POSITION.isEqualTo('below'), EditorContextKeys.isEmbeddedDiffEditor.negate()),143primary: KeyMod.CtrlCmd | KeyCode.UpArrow,144}]145});146}147148override runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) {149InlineChatController.get(editor)?.focus();150}151}152153//#region --- VERSION 1154155export class UnstashSessionAction extends EditorAction2 {156constructor() {157super({158id: 'inlineChat.unstash',159title: localize2('unstash', "Resume Last Dismissed Inline Chat"),160category: AbstractInline1ChatAction.category,161precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_STASHED_SESSION, EditorContextKeys.writable),162keybinding: {163weight: KeybindingWeight.WorkbenchContrib,164primary: KeyMod.CtrlCmd | KeyCode.KeyZ,165}166});167}168169override async runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) {170const ctrl = InlineChatController1.get(editor);171if (ctrl) {172const session = ctrl.unstashLastSession();173if (session) {174ctrl.run({175existingSession: session,176});177}178}179}180}181182export abstract class AbstractInline1ChatAction extends EditorAction2 {183184static readonly category = localize2('cat', "Inline Chat");185186constructor(desc: IAction2Options) {187188const massageMenu = (menu: IAction2Options['menu'] | undefined) => {189if (Array.isArray(menu)) {190for (const entry of menu) {191entry.when = ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_AGENT, entry.when);192}193} else if (menu) {194menu.when = ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_AGENT, menu.when);195}196};197if (Array.isArray(desc.menu)) {198massageMenu(desc.menu);199} else {200massageMenu(desc.menu);201}202203super({204...desc,205category: AbstractInline1ChatAction.category,206precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_AGENT, desc.precondition)207});208}209210override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) {211const editorService = accessor.get(IEditorService);212const logService = accessor.get(ILogService);213214let ctrl = InlineChatController1.get(editor);215if (!ctrl) {216const { activeTextEditorControl } = editorService;217if (isCodeEditor(activeTextEditorControl)) {218editor = activeTextEditorControl;219} else if (isDiffEditor(activeTextEditorControl)) {220editor = activeTextEditorControl.getModifiedEditor();221}222ctrl = InlineChatController1.get(editor);223}224225if (!ctrl) {226logService.warn('[IE] NO controller found for action', this.desc.id, editor.getModel()?.uri);227return;228}229230if (editor instanceof EmbeddedCodeEditorWidget) {231editor = editor.getParentEditor();232}233if (!ctrl) {234for (const diffEditor of accessor.get(ICodeEditorService).listDiffEditors()) {235if (diffEditor.getOriginalEditor() === editor || diffEditor.getModifiedEditor() === editor) {236if (diffEditor instanceof EmbeddedDiffEditorWidget) {237this.runEditorCommand(accessor, diffEditor.getParentEditor(), ..._args);238}239}240}241return;242}243this.runInlineChatCommand(accessor, ctrl, editor, ..._args);244}245246abstract runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController1, editor: ICodeEditor, ...args: any[]): void;247}248249export class ArrowOutUpAction extends AbstractInline1ChatAction {250constructor() {251super({252id: 'inlineChat.arrowOutUp',253title: localize('arrowUp', 'Cursor Up'),254precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, EditorContextKeys.isEmbeddedDiffEditor.negate(), CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()),255keybinding: {256weight: KeybindingWeight.EditorCore,257primary: KeyMod.CtrlCmd | KeyCode.UpArrow258}259});260}261262runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: any[]): void {263ctrl.arrowOut(true);264}265}266267export class ArrowOutDownAction extends AbstractInline1ChatAction {268constructor() {269super({270id: 'inlineChat.arrowOutDown',271title: localize('arrowDown', 'Cursor Down'),272precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_LAST, EditorContextKeys.isEmbeddedDiffEditor.negate(), CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate()),273keybinding: {274weight: KeybindingWeight.EditorCore,275primary: KeyMod.CtrlCmd | KeyCode.DownArrow276}277});278}279280runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: any[]): void {281ctrl.arrowOut(false);282}283}284285export class AcceptChanges extends AbstractInline1ChatAction {286287constructor() {288super({289id: ACTION_ACCEPT_CHANGES,290title: localize2('apply1', "Accept Changes"),291shortTitle: localize('apply2', 'Accept'),292icon: Codicon.check,293f1: true,294precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE),295keybinding: [{296weight: KeybindingWeight.WorkbenchContrib + 10,297primary: KeyMod.CtrlCmd | KeyCode.Enter,298}],299menu: [{300id: MENU_INLINE_CHAT_WIDGET_STATUS,301group: '0_main',302order: 1,303when: ContextKeyExpr.and(304ChatContextKeys.inputHasText.toNegated(),305CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.toNegated(),306CTX_INLINE_CHAT_RESPONSE_TYPE.isEqualTo(InlineChatResponseType.MessagesAndEdits)307),308}, {309id: MENU_INLINE_CHAT_ZONE,310group: 'navigation',311order: 1,312}]313});314}315316override async runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, hunk?: HunkInformation | any): Promise<void> {317ctrl.acceptHunk(hunk);318}319}320321export class DiscardHunkAction extends AbstractInline1ChatAction {322323constructor() {324super({325id: ACTION_DISCARD_CHANGES,326title: localize('discard', 'Discard'),327icon: Codicon.chromeClose,328precondition: CTX_INLINE_CHAT_VISIBLE,329menu: [{330id: MENU_INLINE_CHAT_ZONE,331group: 'navigation',332order: 2333}],334keybinding: {335weight: KeybindingWeight.EditorContrib,336primary: KeyCode.Escape,337when: CTX_INLINE_CHAT_RESPONSE_TYPE.isEqualTo(InlineChatResponseType.MessagesAndEdits)338}339});340}341342async runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, hunk?: HunkInformation | any): Promise<void> {343return ctrl.discardHunk(hunk);344}345}346347export class RerunAction extends AbstractInline1ChatAction {348constructor() {349super({350id: ACTION_REGENERATE_RESPONSE,351title: localize2('chat.rerun.label', "Rerun Request"),352shortTitle: localize('rerun', 'Rerun'),353f1: false,354icon: Codicon.refresh,355precondition: CTX_INLINE_CHAT_VISIBLE,356menu: {357id: MENU_INLINE_CHAT_WIDGET_STATUS,358group: '0_main',359order: 5,360when: ContextKeyExpr.and(361ChatContextKeys.inputHasText.toNegated(),362CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.negate(),363CTX_INLINE_CHAT_RESPONSE_TYPE.notEqualsTo(InlineChatResponseType.None)364)365},366keybinding: {367weight: KeybindingWeight.WorkbenchContrib,368primary: KeyMod.CtrlCmd | KeyCode.KeyR369}370});371}372373override async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: any[]): Promise<void> {374const chatService = accessor.get(IChatService);375const chatWidgetService = accessor.get(IChatWidgetService);376const model = ctrl.chatWidget.viewModel?.model;377if (!model) {378return;379}380381const lastRequest = model.getRequests().at(-1);382if (lastRequest) {383const widget = chatWidgetService.getWidgetBySessionId(model.sessionId);384await chatService.resendRequest(lastRequest, {385noCommandDetection: false,386attempt: lastRequest.attempt + 1,387location: ctrl.chatWidget.location,388userSelectedModelId: widget?.input.currentLanguageModel389});390}391}392}393394export class CloseAction extends AbstractInline1ChatAction {395396constructor() {397super({398id: 'inlineChat.close',399title: localize('close', 'Close'),400icon: Codicon.close,401precondition: CTX_INLINE_CHAT_VISIBLE,402keybinding: {403weight: KeybindingWeight.EditorContrib + 1,404primary: KeyCode.Escape,405},406menu: [{407id: MENU_INLINE_CHAT_WIDGET_STATUS,408group: '0_main',409order: 1,410when: CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.negate()411}, {412id: MENU_INLINE_CHAT_SIDE,413group: 'navigation',414when: CTX_INLINE_CHAT_RESPONSE_TYPE.isEqualTo(InlineChatResponseType.None)415}]416});417}418419async runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: any[]): Promise<void> {420ctrl.cancelSession();421}422}423424export class ConfigureInlineChatAction extends AbstractInline1ChatAction {425constructor() {426super({427id: 'inlineChat.configure',428title: localize2('configure', 'Configure Inline Chat'),429icon: Codicon.settingsGear,430precondition: CTX_INLINE_CHAT_VISIBLE,431f1: true,432menu: {433id: MENU_INLINE_CHAT_WIDGET_STATUS,434group: 'zzz',435order: 5436}437});438}439440async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: any[]): Promise<void> {441accessor.get(IPreferencesService).openSettings({ query: 'inlineChat' });442}443}444445export class MoveToNextHunk extends AbstractInline1ChatAction {446447constructor() {448super({449id: 'inlineChat.moveToNextHunk',450title: localize2('moveToNextHunk', 'Move to Next Change'),451precondition: CTX_INLINE_CHAT_VISIBLE,452f1: true,453keybinding: {454weight: KeybindingWeight.WorkbenchContrib,455primary: KeyCode.F7456}457});458}459460override runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController1, editor: ICodeEditor, ...args: any[]): void {461ctrl.moveHunk(true);462}463}464465export class MoveToPreviousHunk extends AbstractInline1ChatAction {466467constructor() {468super({469id: 'inlineChat.moveToPreviousHunk',470title: localize2('moveToPreviousHunk', 'Move to Previous Change'),471f1: true,472precondition: CTX_INLINE_CHAT_VISIBLE,473keybinding: {474weight: KeybindingWeight.WorkbenchContrib,475primary: KeyMod.Shift | KeyCode.F7476}477});478}479480override runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController1, editor: ICodeEditor, ...args: any[]): void {481ctrl.moveHunk(false);482}483}484485export class ViewInChatAction extends AbstractInline1ChatAction {486constructor() {487super({488id: ACTION_VIEW_IN_CHAT,489title: localize('viewInChat', 'View in Chat'),490icon: Codicon.chatSparkle,491precondition: CTX_INLINE_CHAT_VISIBLE,492menu: [{493id: MENU_INLINE_CHAT_WIDGET_STATUS,494group: 'more',495order: 1,496when: CTX_INLINE_CHAT_RESPONSE_TYPE.notEqualsTo(InlineChatResponseType.Messages)497}, {498id: MENU_INLINE_CHAT_WIDGET_STATUS,499group: '0_main',500order: 1,501when: ContextKeyExpr.and(502ChatContextKeys.inputHasText.toNegated(),503CTX_INLINE_CHAT_RESPONSE_TYPE.isEqualTo(InlineChatResponseType.Messages),504CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.negate()505)506}],507keybinding: {508weight: KeybindingWeight.WorkbenchContrib,509primary: KeyMod.CtrlCmd | KeyCode.DownArrow,510when: ChatContextKeys.inChatInput511}512});513}514override runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, ..._args: any[]) {515return ctrl.viewInChat();516}517}518519export class ToggleDiffForChange extends AbstractInline1ChatAction {520521constructor() {522super({523id: ACTION_TOGGLE_DIFF,524precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_CHANGE_HAS_DIFF),525title: localize2('showChanges', 'Toggle Changes'),526icon: Codicon.diffSingle,527toggled: {528condition: CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF,529},530menu: [{531id: MENU_INLINE_CHAT_WIDGET_STATUS,532group: 'zzz',533order: 1,534}, {535id: MENU_INLINE_CHAT_ZONE,536group: 'navigation',537when: CTX_INLINE_CHAT_CHANGE_HAS_DIFF,538order: 2539}]540});541}542543override runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController1, _editor: ICodeEditor, hunkInfo: HunkInformation | any): void {544ctrl.toggleDiff(hunkInfo);545}546}547548//#endregion549550551//#region --- VERSION 2552abstract class AbstractInline2ChatAction extends EditorAction2 {553554static readonly category = localize2('cat', "Inline Chat");555556constructor(desc: IAction2Options) {557const massageMenu = (menu: IAction2Options['menu'] | undefined) => {558if (Array.isArray(menu)) {559for (const entry of menu) {560entry.when = ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_AGENT2, entry.when);561}562} else if (menu) {563menu.when = ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_AGENT2, menu.when);564}565};566if (Array.isArray(desc.menu)) {567massageMenu(desc.menu);568} else {569massageMenu(desc.menu);570}571572super({573...desc,574category: AbstractInline2ChatAction.category,575precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_AGENT2, desc.precondition)576});577}578579override runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ..._args: any[]) {580const editorService = accessor.get(IEditorService);581const logService = accessor.get(ILogService);582583let ctrl = InlineChatController2.get(editor);584if (!ctrl) {585const { activeTextEditorControl } = editorService;586if (isCodeEditor(activeTextEditorControl)) {587editor = activeTextEditorControl;588} else if (isDiffEditor(activeTextEditorControl)) {589editor = activeTextEditorControl.getModifiedEditor();590}591ctrl = InlineChatController2.get(editor);592}593594if (!ctrl) {595logService.warn('[IE] NO controller found for action', this.desc.id, editor.getModel()?.uri);596return;597}598599if (editor instanceof EmbeddedCodeEditorWidget) {600editor = editor.getParentEditor();601}602if (!ctrl) {603for (const diffEditor of accessor.get(ICodeEditorService).listDiffEditors()) {604if (diffEditor.getOriginalEditor() === editor || diffEditor.getModifiedEditor() === editor) {605if (diffEditor instanceof EmbeddedDiffEditorWidget) {606this.runEditorCommand(accessor, diffEditor.getParentEditor(), ..._args);607}608}609}610return;611}612this.runInlineChatCommand(accessor, ctrl, editor, ..._args);613}614615abstract runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController2, editor: ICodeEditor, ...args: any[]): void;616}617618class KeepOrUndoSessionAction extends AbstractInline2ChatAction {619620constructor(id: string, private readonly _keep: boolean) {621super({622id,623title: _keep624? localize2('Keep', "Keep")625: localize2('Undo', "Undo"),626f1: true,627icon: _keep ? Codicon.check : Codicon.discard,628precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE, ctxHasRequestInProgress.negate()),629keybinding: [{630weight: KeybindingWeight.WorkbenchContrib + 10, // win over new-window-action631primary: _keep632? KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyY633: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyN634}],635menu: [{636id: MENU_INLINE_CHAT_WIDGET_STATUS,637group: '0_main',638order: 1,639when: ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_AGENT2, ContextKeyExpr.greater(ctxRequestCount.key, 0), ctxHasEditorModification),640}]641});642}643644override async runInlineChatCommand(accessor: ServicesAccessor, _ctrl: InlineChatController2, editor: ICodeEditor, ..._args: any[]): Promise<void> {645const inlineChatSessions = accessor.get(IInlineChatSessionService);646if (!editor.hasModel()) {647return;648}649const textModel = editor.getModel();650const session = inlineChatSessions.getSession2(textModel.uri);651if (session) {652if (this._keep) {653await session.editingSession.accept();654} else {655await session.editingSession.reject();656}657session.dispose();658}659}660}661662export class KeepSessionAction2 extends KeepOrUndoSessionAction {663constructor() {664super('inlineChat2.keep', true);665}666}667668export class UndoSessionAction2 extends KeepOrUndoSessionAction {669constructor() {670super('inlineChat2.undo', false);671}672}673674export class CloseSessionAction2 extends AbstractInline2ChatAction {675676constructor() {677super({678id: 'inlineChat2.close',679title: localize2('close2', "Close"),680f1: true,681icon: Codicon.close,682precondition: ContextKeyExpr.and(683CTX_INLINE_CHAT_VISIBLE,684ctxHasRequestInProgress.negate(),685ContextKeyExpr.or(ctxRequestCount.isEqualTo(0), ctxHasEditorModification.negate())686),687keybinding: [{688when: ctxRequestCount.isEqualTo(0),689weight: KeybindingWeight.WorkbenchContrib,690primary: KeyMod.CtrlCmd | KeyCode.KeyI,691}, {692weight: KeybindingWeight.WorkbenchContrib,693primary: KeyCode.Escape,694}],695menu: [{696id: MENU_INLINE_CHAT_SIDE,697group: 'navigation',698when: ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_AGENT2, ctxRequestCount.isEqualTo(0)),699}, {700id: MENU_INLINE_CHAT_WIDGET_STATUS,701group: '0_main',702order: 1,703when: ContextKeyExpr.and(CTX_INLINE_CHAT_HAS_AGENT2, ctxHasEditorModification.negate()),704}]705});706}707708runInlineChatCommand(accessor: ServicesAccessor, _ctrl: InlineChatController2, editor: ICodeEditor, ...args: any[]): void {709const inlineChatSessions = accessor.get(IInlineChatSessionService);710if (editor.hasModel()) {711const textModel = editor.getModel();712inlineChatSessions.getSession2(textModel.uri)?.dispose();713}714}715}716717export class RevealWidget extends AbstractInline2ChatAction {718constructor() {719super({720id: 'inlineChat2.reveal',721title: localize2('reveal', "Toggle Inline Chat"),722f1: true,723icon: Codicon.chatSparkle,724precondition: ContextKeyExpr.and(ctxIsGlobalEditingSession.negate(), ContextKeyExpr.greaterEquals(ctxRequestCount.key, 1)),725toggled: {726condition: CTX_INLINE_CHAT_VISIBLE,727},728keybinding: {729weight: KeybindingWeight.WorkbenchContrib,730primary: KeyMod.CtrlCmd | KeyCode.KeyI731},732menu: {733id: MenuId.ChatEditingEditorContent,734when: ContextKeyExpr.and(735ContextKeyExpr.greaterEquals(ctxRequestCount.key, 1),736ctxIsGlobalEditingSession.negate(),737),738group: 'navigate',739order: 4,740}741});742}743744runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController2, _editor: ICodeEditor): void {745ctrl.toggleWidgetUntilNextRequest();746ctrl.markActiveController();747}748}749750export class CancelRequestAction extends AbstractInline2ChatAction {751constructor() {752super({753id: 'inlineChat2.cancelRequest',754title: localize2('cancel', "Cancel Request"),755f1: true,756icon: Codicon.stopCircle,757precondition: ContextKeyExpr.and(ctxIsGlobalEditingSession.negate(), ctxHasRequestInProgress),758toggled: CTX_INLINE_CHAT_VISIBLE,759menu: {760id: MenuId.ChatEditingEditorContent,761when: ContextKeyExpr.and(ctxIsGlobalEditingSession.negate(), ctxHasRequestInProgress),762group: 'a_request',763order: 1,764}765});766}767768runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController2, _editor: ICodeEditor): void {769const chatService = accessor.get(IChatService);770771const { viewModel } = ctrl.widget.chatWidget;772if (viewModel) {773ctrl.toggleWidgetUntilNextRequest();774ctrl.markActiveController();775chatService.cancelCurrentRequestForSession(viewModel.sessionId);776}777}778}779780781