mirror of
https://github.com/microsoft/vscode.git
synced 2026-10-07 01:08:40 +01:00
1 parent
3609a92bb0
commit
4e5ed5dba9
6 files changed
+280
-335
No files matched your search
+221
-317
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { h } from '../../../../../../../base/browser/dom.js';
|
||||
import { ActionBar } from '../../../../../../../base/browser/ui/actionbar/actionbar.js';
|
||||
import { isMarkdownString, MarkdownString } from '../../../../../../../base/common/htmlContent.js';
|
||||
import { IConfigurationService } from '../../../../../../../platform/configuration/common/configuration.js';
|
||||
import { IInstantiationService } from '../../../../../../../platform/instantiation/common/instantiation.js';
|
||||
@@ -25,7 +24,7 @@ import { ChatCollapsibleContentPart } from '../chatCollapsibleContentPart.js';
|
||||
import { IChatRendererContent } from '../../../../common/model/chatViewModel.js';
|
||||
import '../media/chatTerminalToolProgressPart.css';
|
||||
import type { ICodeBlockRenderOptions } from '../codeBlockPart.js';
|
||||
import { Action, IAction } from '../../../../../../../base/common/actions.js';
|
||||
import { IAction } from '../../../../../../../base/common/actions.js';
|
||||
import { timeout } from '../../../../../../../base/common/async.js';
|
||||
import { IChatTerminalToolProgressPart, ITerminalChatService, ITerminalConfigurationService, ITerminalEditorService, ITerminalGroupService, ITerminalInstance, ITerminalService } from '../../../../../terminal/browser/terminal.js';
|
||||
import { Disposable, DisposableStore, MutableDisposable, toDisposable, type IDisposable } from '../../../../../../../base/common/lifecycle.js';
|
||||
@@ -58,6 +57,11 @@ import { removeAnsiEscapeCodes } from '../../../../../../../base/common/strings.
|
||||
import { PANEL_BACKGROUND } from '../../../../../../common/theme.js';
|
||||
import { editorBackground } from '../../../../../../../platform/theme/common/colorRegistry.js';
|
||||
import { IThemeService } from '../../../../../../../platform/theme/common/themeService.js';
|
||||
import { MenuWorkbenchToolBar } from '../../../../../../../platform/actions/browser/toolbar.js';
|
||||
import { MenuRegistry } from '../../../../../../../platform/actions/common/actions.js';
|
||||
import { CommandsRegistry } from '../../../../../../../platform/commands/common/commands.js';
|
||||
import { MENU_CHAT_TERMINAL_TOOL_PROGRESS, TerminalChatContextKeys } from '../../../../../terminal/terminalContribChatExports.js';
|
||||
import { ServiceCollection } from '../../../../../../../platform/instantiation/common/serviceCollection.js';
|
||||
|
||||
/**
|
||||
* Minimum number of rows to display in the terminal output view.
|
||||
@@ -94,6 +98,62 @@ const MIN_DATA_EVENTS_FOR_REAL_OUTPUT = 2;
|
||||
*/
|
||||
const expandedStateByInvocation = new WeakMap<IChatToolInvocation | IChatToolInvocationSerialized, boolean>();
|
||||
|
||||
// --- Command and menu registrations for terminal tool progress toolbar ---
|
||||
|
||||
CommandsRegistry.registerCommand(TerminalContribCommandId.FocusChatInstanceAction, async (_accessor: unknown, progressPart?: IChatTerminalToolProgressPart) => {
|
||||
await progressPart?.focusTerminal();
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand(TerminalContribCommandId.ContinueInBackground, async (_accessor: unknown, progressPart?: IChatTerminalToolProgressPart) => {
|
||||
progressPart?.continueInBackground();
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand(TerminalContribCommandId.ToggleChatTerminalOutput, async (_accessor: unknown, progressPart?: IChatTerminalToolProgressPart) => {
|
||||
await progressPart?.toggleOutputFromAction();
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MENU_CHAT_TERMINAL_TOOL_PROGRESS, {
|
||||
command: {
|
||||
id: TerminalContribCommandId.ContinueInBackground,
|
||||
title: localize('continueInBackground', 'Continue in Background'),
|
||||
icon: Codicon.debugContinue,
|
||||
},
|
||||
when: TerminalChatContextKeys.chatToolCanContinueInBackground,
|
||||
order: 0,
|
||||
group: 'navigation',
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MENU_CHAT_TERMINAL_TOOL_PROGRESS, {
|
||||
command: {
|
||||
id: TerminalContribCommandId.FocusChatInstanceAction,
|
||||
title: localize('focusTerminal', 'Focus Terminal'),
|
||||
icon: Codicon.openInProduct,
|
||||
toggled: {
|
||||
condition: TerminalChatContextKeys.chatToolIsHiddenTerminal,
|
||||
title: localize('showTerminal', 'Show and Focus Terminal'),
|
||||
}
|
||||
},
|
||||
when: TerminalChatContextKeys.chatToolHasInstance,
|
||||
order: 1,
|
||||
group: 'navigation',
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MENU_CHAT_TERMINAL_TOOL_PROGRESS, {
|
||||
command: {
|
||||
id: TerminalContribCommandId.ToggleChatTerminalOutput,
|
||||
title: localize('showTerminalOutput', 'Show Output'),
|
||||
icon: Codicon.chevronRight,
|
||||
toggled: {
|
||||
condition: TerminalChatContextKeys.chatToolOutputExpanded,
|
||||
title: localize('hideTerminalOutput', 'Hide Output'),
|
||||
icon: Codicon.chevronDown,
|
||||
}
|
||||
},
|
||||
when: TerminalChatContextKeys.chatToolHasOutput.isEqualTo(true),
|
||||
order: 2,
|
||||
group: 'navigation',
|
||||
});
|
||||
|
||||
/**
|
||||
* Options for configuring a terminal command decoration.
|
||||
*/
|
||||
@@ -247,8 +307,6 @@ class TerminalCommandDecoration extends Disposable {
|
||||
export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart implements IChatTerminalToolProgressPart {
|
||||
public readonly domNode: HTMLElement;
|
||||
|
||||
private readonly _actionBar: ActionBar;
|
||||
|
||||
private readonly _titleElement: HTMLElement;
|
||||
private readonly _outputView: ChatTerminalToolOutputSection;
|
||||
private readonly _terminalOutputContextKey: IContextKey<boolean>;
|
||||
@@ -257,14 +315,15 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
private readonly _contentIndex: number;
|
||||
private readonly _sessionResource: URI;
|
||||
|
||||
private readonly _showOutputAction = this._register(new MutableDisposable<ToggleChatTerminalOutputAction>());
|
||||
private _showOutputActionAdded = false;
|
||||
private readonly _focusAction = this._register(new MutableDisposable<FocusChatInstanceAction>());
|
||||
private readonly _continueInBackgroundAction = this._register(new MutableDisposable<ContinueInBackgroundAction>());
|
||||
// Scoped context keys that drive toolbar action visibility
|
||||
private readonly _hasInstanceKey: IContextKey<boolean>;
|
||||
private readonly _canContinueInBackgroundKey: IContextKey<boolean>;
|
||||
private readonly _hasOutputKey: IContextKey<boolean>;
|
||||
private readonly _isHiddenTerminalKey: IContextKey<boolean>;
|
||||
private readonly _outputExpandedKey: IContextKey<boolean>;
|
||||
|
||||
private readonly _terminalData: IChatTerminalToolInvocationData;
|
||||
private _terminalCommandUri: URI | undefined;
|
||||
private _storedCommandId: string | undefined;
|
||||
private readonly _commandText: string;
|
||||
private readonly _isSerializedInvocation: boolean;
|
||||
private _terminalInstance: ITerminalInstance | undefined;
|
||||
@@ -302,6 +361,9 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
@IChatWidgetService private readonly _chatWidgetService: IChatWidgetService,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@ITerminalEditorService private readonly _terminalEditorService: ITerminalEditorService,
|
||||
@ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
) {
|
||||
super(toolInvocation);
|
||||
|
||||
@@ -312,7 +374,6 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
terminalData = migrateLegacyTerminalToolSpecificData(terminalData);
|
||||
this._terminalData = terminalData;
|
||||
this._terminalCommandUri = terminalData.terminalCommandUri ? URI.revive(terminalData.terminalCommandUri) : undefined;
|
||||
this._storedCommandId = this._terminalCommandUri ? new URLSearchParams(this._terminalCommandUri.query ?? '').get('command') ?? undefined : undefined;
|
||||
this._isSerializedInvocation = (toolInvocation.kind === 'toolInvocationSerialized');
|
||||
|
||||
const elements = h('.chat-terminal-content-part@container', [
|
||||
@@ -368,17 +429,52 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
this._register(this._outputView.onDidFocus(() => this._handleOutputFocus()));
|
||||
this._register(this._outputView.onDidBlur(e => this._handleOutputBlur(e)));
|
||||
this._register(toDisposable(() => this._handleDispose()));
|
||||
this._register(this._keybindingService.onDidUpdateKeybindings(() => {
|
||||
this._focusAction.value?.refreshKeybindingTooltip();
|
||||
this._showOutputAction.value?.refreshKeybindingTooltip();
|
||||
}));
|
||||
|
||||
|
||||
// Create a scoped context key service for this toolbar so each progress part's
|
||||
// context keys are independent from other parts.
|
||||
const actionBarEl = h('.chat-terminal-action-bar@actionBar');
|
||||
elements.title.append(actionBarEl.root);
|
||||
this._actionBar = this._register(new ActionBar(actionBarEl.actionBar, {}));
|
||||
const toolbarContextKeyService = this._register(this._contextKeyService.createScoped(actionBarEl.actionBar));
|
||||
this._hasInstanceKey = TerminalChatContextKeys.chatToolHasInstance.bindTo(toolbarContextKeyService);
|
||||
this._canContinueInBackgroundKey = TerminalChatContextKeys.chatToolCanContinueInBackground.bindTo(toolbarContextKeyService);
|
||||
this._hasOutputKey = TerminalChatContextKeys.chatToolHasOutput.bindTo(toolbarContextKeyService);
|
||||
this._isHiddenTerminalKey = TerminalChatContextKeys.chatToolIsHiddenTerminal.bindTo(toolbarContextKeyService);
|
||||
this._outputExpandedKey = TerminalChatContextKeys.chatToolOutputExpanded.bindTo(toolbarContextKeyService);
|
||||
const usesCollapsibleKey = TerminalChatContextKeys.chatToolUsesCollapsible.bindTo(toolbarContextKeyService);
|
||||
|
||||
const scopedInstantiationService = this._register(this._instantiationService.createChild(
|
||||
new ServiceCollection([IContextKeyService, toolbarContextKeyService])
|
||||
));
|
||||
this._register(scopedInstantiationService.createInstance(
|
||||
MenuWorkbenchToolBar,
|
||||
actionBarEl.actionBar,
|
||||
MENU_CHAT_TERMINAL_TOOL_PROGRESS,
|
||||
{
|
||||
menuOptions: { arg: this, shouldForwardArgs: true },
|
||||
getKeyBinding: (action: IAction) => {
|
||||
if (action.id === TerminalContribCommandId.FocusChatInstanceAction) {
|
||||
return this._keybindingService.lookupKeybinding(TerminalContribCommandId.FocusMostRecentChatTerminal);
|
||||
}
|
||||
if (action.id === TerminalContribCommandId.ToggleChatTerminalOutput) {
|
||||
return this._keybindingService.lookupKeybinding(TerminalContribCommandId.FocusMostRecentChatTerminalOutput);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
}
|
||||
));
|
||||
this._initializeTerminalActions();
|
||||
this._terminalService.whenConnected.then(() => this._initializeTerminalActions());
|
||||
|
||||
// Listen for continue in background — sets context key so toolbar auto-hides the action
|
||||
const terminalToolSessionId = this._terminalData.terminalToolSessionId;
|
||||
if (terminalToolSessionId) {
|
||||
this._register(this._terminalChatService.onDidContinueInBackground(sessionId => {
|
||||
if (sessionId === terminalToolSessionId) {
|
||||
this._terminalData.didContinueInBackground = true;
|
||||
this._canContinueInBackgroundKey.set(false);
|
||||
}
|
||||
}));
|
||||
}
|
||||
let pastTenseMessage: string | undefined;
|
||||
if (toolInvocation.pastTenseMessage) {
|
||||
pastTenseMessage = `${typeof toolInvocation.pastTenseMessage === 'string' ? toolInvocation.pastTenseMessage : toolInvocation.pastTenseMessage.value}`;
|
||||
@@ -420,6 +516,7 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
const requiresConfirmation = toolInvocation.kind === 'toolInvocation' && IChatToolInvocation.getConfirmationMessages(toolInvocation);
|
||||
this._isInThinkingContainer = terminalToolsInThinking && !requiresConfirmation;
|
||||
this._usesCollapsibleWrapper = this._isInThinkingContainer || isSimpleTerminal;
|
||||
usesCollapsibleKey.set(this._usesCollapsibleWrapper);
|
||||
|
||||
if (this._usesCollapsibleWrapper) {
|
||||
this.domNode = this._createCollapsibleWrapper(progressPart.domNode, displayCommand, toolInvocation, context);
|
||||
@@ -539,7 +636,7 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
}
|
||||
const terminalToolSessionId = this._terminalData.terminalToolSessionId;
|
||||
if (!terminalToolSessionId) {
|
||||
this._addActions();
|
||||
this._updateToolbarContextKeys();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -551,7 +648,7 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
if (this._isSerializedInvocation) {
|
||||
this._clearCommandAssociation();
|
||||
}
|
||||
this._addActions(undefined, terminalToolSessionId);
|
||||
this._updateToolbarContextKeys(undefined, terminalToolSessionId);
|
||||
return;
|
||||
}
|
||||
const isNewInstance = this._terminalInstance !== instance;
|
||||
@@ -559,16 +656,14 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
this._terminalInstance = instance;
|
||||
this._registerInstanceListener(instance);
|
||||
}
|
||||
// Always call _addActions to ensure actions are added, even if instance was set earlier
|
||||
// (e.g., by the output view during expanded state restoration)
|
||||
this._addActions(instance, terminalToolSessionId);
|
||||
this._updateToolbarContextKeys(instance, terminalToolSessionId);
|
||||
};
|
||||
|
||||
const initialInstance = await this._terminalChatService.getTerminalInstanceByToolSessionId(terminalToolSessionId);
|
||||
await attachInstance(initialInstance);
|
||||
|
||||
if (!initialInstance) {
|
||||
this._addActions(undefined, terminalToolSessionId);
|
||||
this._updateToolbarContextKeys(undefined, terminalToolSessionId);
|
||||
}
|
||||
|
||||
if (this._store.isDisposed) {
|
||||
@@ -587,45 +682,50 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
});
|
||||
this._terminalSessionRegistration = this._store.add(listener);
|
||||
}
|
||||
|
||||
// Listen for continue in background to remove the button
|
||||
this._store.add(this._terminalChatService.onDidContinueInBackground(sessionId => {
|
||||
if (sessionId === terminalToolSessionId) {
|
||||
this._terminalData.didContinueInBackground = true;
|
||||
this._removeContinueInBackgroundAction();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private _addActions(terminalInstance?: ITerminalInstance, terminalToolSessionId?: string): void {
|
||||
/**
|
||||
* Updates the scoped context keys that drive toolbar action visibility.
|
||||
* The `MenuWorkbenchToolBar` automatically shows/hides actions based on these keys.
|
||||
*/
|
||||
private _updateToolbarContextKeys(terminalInstance?: ITerminalInstance, terminalToolSessionId?: string): void {
|
||||
if (this._store.isDisposed) {
|
||||
return;
|
||||
}
|
||||
const actionBar = this._actionBar;
|
||||
this._removeFocusAction();
|
||||
const resolvedCommand = this._getResolvedCommand(terminalInstance);
|
||||
|
||||
this._removeContinueInBackgroundAction();
|
||||
if (terminalInstance) {
|
||||
const isTerminalHidden = terminalInstance && terminalToolSessionId ? this._terminalChatService.isBackgroundTerminal(terminalToolSessionId) : false;
|
||||
const focusAction = this._instantiationService.createInstance(FocusChatInstanceAction, terminalInstance, resolvedCommand, this._terminalCommandUri, this._storedCommandId, isTerminalHidden);
|
||||
this._focusAction.value = focusAction;
|
||||
actionBar.push(focusAction, { icon: true, label: false, index: 0 });
|
||||
// Focus terminal action
|
||||
this._hasInstanceKey.set(!!terminalInstance);
|
||||
if (terminalInstance && terminalToolSessionId) {
|
||||
this._isHiddenTerminalKey.set(this._terminalChatService.isBackgroundTerminal(terminalToolSessionId));
|
||||
} else {
|
||||
this._isHiddenTerminalKey.set(false);
|
||||
}
|
||||
|
||||
// Add continue in background action - only for foreground executions with running commands
|
||||
// Note: isBackground refers to whether the tool was invoked with isBackground=true (background execution),
|
||||
// not whether the terminal is hidden from the user
|
||||
if (terminalToolSessionId && !this._terminalData.isBackground && !this._terminalData.didContinueInBackground) {
|
||||
const isStillRunning = resolvedCommand?.exitCode === undefined && this._terminalData.terminalCommandState?.exitCode === undefined;
|
||||
if (isStillRunning) {
|
||||
const continueAction = this._instantiationService.createInstance(ContinueInBackgroundAction, terminalToolSessionId);
|
||||
this._continueInBackgroundAction.value = continueAction;
|
||||
actionBar.push(continueAction, { icon: true, label: false, index: 0 });
|
||||
// Continue in background action
|
||||
if (terminalInstance && terminalToolSessionId && !this._terminalData.isBackground && !this._terminalData.didContinueInBackground) {
|
||||
const isStillRunning = resolvedCommand?.exitCode === undefined && this._terminalData.terminalCommandState?.exitCode === undefined;
|
||||
this._canContinueInBackgroundKey.set(isStillRunning);
|
||||
} else {
|
||||
this._canContinueInBackgroundKey.set(false);
|
||||
}
|
||||
|
||||
// Show output action (only when NOT using collapsible wrapper)
|
||||
if (!this._usesCollapsibleWrapper) {
|
||||
const hasSnapshot = !!this._terminalData.terminalCommandOutput;
|
||||
const hasOutput = !!resolvedCommand || hasSnapshot;
|
||||
this._hasOutputKey.set(hasOutput);
|
||||
|
||||
// Auto-expand on first detection of failed output
|
||||
if (hasOutput && !this._outputView.isExpanded) {
|
||||
const autoExpandFailures = this._configurationService.getValue<boolean>(ChatConfiguration.AutoExpandToolFailures);
|
||||
const exitCode = resolvedCommand?.exitCode ?? this._terminalData.terminalCommandState?.exitCode;
|
||||
if (exitCode !== undefined && exitCode !== 0 && autoExpandFailures) {
|
||||
this._toggleOutput(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._ensureShowOutputAction(resolvedCommand);
|
||||
this._decoration.update(resolvedCommand);
|
||||
}
|
||||
|
||||
@@ -637,52 +737,8 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
return this._resolveCommand(target);
|
||||
}
|
||||
|
||||
private _ensureShowOutputAction(command?: ITerminalCommand): void {
|
||||
if (this._store.isDisposed) {
|
||||
return;
|
||||
}
|
||||
// don't show dropdown when rendered with the simplified/collapsible wrapper
|
||||
if (this._usesCollapsibleWrapper) {
|
||||
return;
|
||||
}
|
||||
const resolvedCommand = command ?? this._getResolvedCommand();
|
||||
const hasSnapshot = !!this._terminalData.terminalCommandOutput;
|
||||
if (!resolvedCommand && !hasSnapshot) {
|
||||
return;
|
||||
}
|
||||
let showOutputAction = this._showOutputAction.value;
|
||||
if (!showOutputAction) {
|
||||
showOutputAction = this._instantiationService.createInstance(ToggleChatTerminalOutputAction, () => this._toggleOutputFromAction());
|
||||
this._showOutputAction.value = showOutputAction;
|
||||
const autoExpandFailures = this._configurationService.getValue<boolean>(ChatConfiguration.AutoExpandToolFailures);
|
||||
const exitCode = resolvedCommand?.exitCode ?? this._terminalData.terminalCommandState?.exitCode;
|
||||
if (exitCode !== undefined && exitCode !== 0 && autoExpandFailures) {
|
||||
this._toggleOutput(true);
|
||||
}
|
||||
}
|
||||
showOutputAction.syncPresentation(this._outputView.isExpanded);
|
||||
|
||||
const actionBar = this._actionBar;
|
||||
if (this._showOutputActionAdded) {
|
||||
const existingIndex = actionBar.viewItems.findIndex(item => item.action === showOutputAction);
|
||||
if (existingIndex >= 0 && existingIndex !== actionBar.length() - 1) {
|
||||
actionBar.pull(existingIndex);
|
||||
this._showOutputActionAdded = false;
|
||||
} else if (existingIndex >= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._showOutputActionAdded) {
|
||||
return;
|
||||
}
|
||||
actionBar.push([showOutputAction], { icon: true, label: false });
|
||||
this._showOutputActionAdded = true;
|
||||
}
|
||||
|
||||
private _clearCommandAssociation(options?: { clearPersistentData?: boolean }): void {
|
||||
this._terminalCommandUri = undefined;
|
||||
this._storedCommandId = undefined;
|
||||
if (options?.clearPersistentData) {
|
||||
if (this._terminalData.terminalCommandUri) {
|
||||
delete this._terminalData.terminalCommandUri;
|
||||
@@ -719,7 +775,7 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
const commandDetectionListener = this._register(new MutableDisposable<IDisposable>());
|
||||
const tryResolveCommand = async (): Promise<ITerminalCommand | undefined> => {
|
||||
const resolvedCommand = this._resolveCommand(terminalInstance);
|
||||
this._addActions(terminalInstance, this._terminalData.terminalToolSessionId);
|
||||
this._updateToolbarContextKeys(terminalInstance, this._terminalData.terminalToolSessionId);
|
||||
return resolvedCommand;
|
||||
};
|
||||
|
||||
@@ -778,11 +834,11 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
}));
|
||||
|
||||
store.add(commandDetection.onCommandExecuted(() => {
|
||||
this._addActions(terminalInstance, this._terminalData.terminalToolSessionId);
|
||||
this._updateToolbarContextKeys(terminalInstance, this._terminalData.terminalToolSessionId);
|
||||
}));
|
||||
|
||||
store.add(commandDetection.onCommandFinished(() => {
|
||||
this._addActions(terminalInstance, this._terminalData.terminalToolSessionId);
|
||||
this._updateToolbarContextKeys(terminalInstance, this._terminalData.terminalToolSessionId);
|
||||
const resolvedCommand = this._getResolvedCommand(terminalInstance);
|
||||
|
||||
this._handleCommandCompletion(resolvedCommand);
|
||||
@@ -810,47 +866,11 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
}
|
||||
this._clearCommandAssociation({ clearPersistentData: true });
|
||||
commandDetectionListener.clear();
|
||||
if (!this._store.isDisposed) {
|
||||
this._actionBar.clear();
|
||||
}
|
||||
this._removeFocusAction();
|
||||
this._showOutputActionAdded = false;
|
||||
this._showOutputAction.clear();
|
||||
this._addActions(undefined, this._terminalData.terminalToolSessionId);
|
||||
this._updateToolbarContextKeys(undefined, this._terminalData.terminalToolSessionId);
|
||||
instanceListener.dispose();
|
||||
}));
|
||||
}
|
||||
|
||||
private _removeFocusAction(): void {
|
||||
if (this._store.isDisposed) {
|
||||
return;
|
||||
}
|
||||
const actionBar = this._actionBar;
|
||||
const focusAction = this._focusAction.value;
|
||||
if (actionBar && focusAction) {
|
||||
const existingIndex = actionBar.viewItems.findIndex(item => item.action === focusAction);
|
||||
if (existingIndex >= 0) {
|
||||
actionBar.pull(existingIndex);
|
||||
}
|
||||
}
|
||||
this._focusAction.clear();
|
||||
}
|
||||
|
||||
private _removeContinueInBackgroundAction(): void {
|
||||
if (this._store.isDisposed) {
|
||||
return;
|
||||
}
|
||||
const actionBar = this._actionBar;
|
||||
const continueAction = this._continueInBackgroundAction.value;
|
||||
if (actionBar && continueAction) {
|
||||
const existingIndex = actionBar.viewItems.findIndex(item => item.action === continueAction);
|
||||
if (existingIndex >= 0) {
|
||||
actionBar.pull(existingIndex);
|
||||
}
|
||||
}
|
||||
this._continueInBackgroundAction.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the completion of a terminal command by updating the UI state.
|
||||
* This includes marking the collapsible wrapper as complete, auto-collapsing
|
||||
@@ -878,7 +898,7 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
const didChange = await this._outputView.toggle(expanded);
|
||||
const isExpanded = this._outputView.isExpanded;
|
||||
this._titleElement.classList.toggle('chat-terminal-content-title-no-bottom-radius', isExpanded);
|
||||
this._showOutputAction.value?.syncPresentation(isExpanded);
|
||||
this._outputExpandedKey.set(isExpanded);
|
||||
if (didChange) {
|
||||
expandedStateByInvocation.set(this.toolInvocation, isExpanded);
|
||||
}
|
||||
@@ -932,15 +952,80 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
}
|
||||
|
||||
public async focusTerminal(): Promise<void> {
|
||||
if (this._focusAction.value) {
|
||||
await this._focusAction.value.run();
|
||||
const instance = this._terminalInstance;
|
||||
|
||||
type FocusChatInstanceTelemetryEvent = {
|
||||
target: 'instance' | 'commandUri' | 'none';
|
||||
location: 'panel' | 'editor';
|
||||
};
|
||||
|
||||
type FocusChatInstanceTelemetryClassification = {
|
||||
owner: 'meganrogge';
|
||||
comment: 'Track usage of the focus chat terminal action.';
|
||||
target: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether focusing targeted an existing instance or opened a command URI.' };
|
||||
location: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Location of the terminal instance when focusing.' };
|
||||
};
|
||||
|
||||
let target: FocusChatInstanceTelemetryEvent['target'] = 'none';
|
||||
let location: FocusChatInstanceTelemetryEvent['location'] = 'panel';
|
||||
if (instance) {
|
||||
target = 'instance';
|
||||
location = instance.target === TerminalLocation.Editor ? 'editor' : 'panel';
|
||||
} else if (this._terminalCommandUri) {
|
||||
target = 'commandUri';
|
||||
}
|
||||
this._telemetryService.publicLog2<FocusChatInstanceTelemetryEvent, FocusChatInstanceTelemetryClassification>('terminal/chatFocusInstance', { target, location });
|
||||
|
||||
if (instance) {
|
||||
this._terminalService.setActiveInstance(instance);
|
||||
if (instance.target === TerminalLocation.Editor) {
|
||||
this._terminalEditorService.openEditor(instance);
|
||||
} else {
|
||||
await this._terminalGroupService.showPanel(true);
|
||||
}
|
||||
this._terminalService.setActiveInstance(instance);
|
||||
await instance.focusWhenReady(true);
|
||||
const command = this._getResolvedCommand(instance);
|
||||
if (command) {
|
||||
instance.xterm?.markTracker.revealCommand(command);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._terminalCommandUri) {
|
||||
this._terminalService.openResource(this._terminalCommandUri);
|
||||
}
|
||||
}
|
||||
|
||||
public continueInBackground(): void {
|
||||
const sessionId = this._terminalData.terminalToolSessionId;
|
||||
if (sessionId) {
|
||||
this._terminalChatService.continueInBackground(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
public async toggleOutputFromAction(): Promise<void> {
|
||||
this._userToggledOutput = true;
|
||||
|
||||
type ToggleChatTerminalOutputTelemetryEvent = {
|
||||
previousExpanded: boolean;
|
||||
};
|
||||
type ToggleChatTerminalOutputTelemetryClassification = {
|
||||
owner: 'meganrogge';
|
||||
comment: 'Track usage of the toggle chat terminal output action.';
|
||||
previousExpanded: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the terminal output was expanded before the toggle.' };
|
||||
};
|
||||
this._telemetryService.publicLog2<ToggleChatTerminalOutputTelemetryEvent, ToggleChatTerminalOutputTelemetryClassification>('terminal/chatToggleOutput', {
|
||||
previousExpanded: this._outputView.isExpanded
|
||||
});
|
||||
|
||||
if (!this._outputView.isExpanded) {
|
||||
await this._toggleOutput(true);
|
||||
return;
|
||||
}
|
||||
await this._toggleOutput(false);
|
||||
}
|
||||
|
||||
public async toggleOutputFromKeyboard(): Promise<void> {
|
||||
this._userToggledOutput = true;
|
||||
if (!this._outputView.isExpanded) {
|
||||
@@ -951,15 +1036,6 @@ export class ChatTerminalToolProgressPart extends BaseChatToolInvocationSubPart
|
||||
await this._collapseOutputAndFocusInput();
|
||||
}
|
||||
|
||||
private async _toggleOutputFromAction(): Promise<void> {
|
||||
this._userToggledOutput = true;
|
||||
if (!this._outputView.isExpanded) {
|
||||
await this._toggleOutput(true);
|
||||
return;
|
||||
}
|
||||
await this._toggleOutput(false);
|
||||
}
|
||||
|
||||
private async _collapseOutputAndFocusInput(): Promise<void> {
|
||||
if (this._outputView.isExpanded) {
|
||||
await this._toggleOutput(false);
|
||||
@@ -1460,178 +1536,6 @@ class ChatTerminalToolOutputSection extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
export class ToggleChatTerminalOutputAction extends Action implements IAction {
|
||||
private _expanded = false;
|
||||
|
||||
constructor(
|
||||
private readonly _toggle: () => Promise<void>,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
) {
|
||||
super(
|
||||
TerminalContribCommandId.ToggleChatTerminalOutput,
|
||||
localize('showTerminalOutput', 'Show Output'),
|
||||
ThemeIcon.asClassName(Codicon.chevronRight),
|
||||
true,
|
||||
);
|
||||
this._updateTooltip();
|
||||
}
|
||||
|
||||
public override async run(): Promise<void> {
|
||||
type ToggleChatTerminalOutputTelemetryEvent = {
|
||||
previousExpanded: boolean;
|
||||
};
|
||||
|
||||
type ToggleChatTerminalOutputTelemetryClassification = {
|
||||
owner: 'meganrogge';
|
||||
comment: 'Track usage of the toggle chat terminal output action.';
|
||||
previousExpanded: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the terminal output was expanded before the toggle.' };
|
||||
};
|
||||
this._telemetryService.publicLog2<ToggleChatTerminalOutputTelemetryEvent, ToggleChatTerminalOutputTelemetryClassification>('terminal/chatToggleOutput', {
|
||||
previousExpanded: this._expanded
|
||||
});
|
||||
await this._toggle();
|
||||
}
|
||||
|
||||
public syncPresentation(expanded: boolean): void {
|
||||
this._expanded = expanded;
|
||||
this._updatePresentation();
|
||||
this._updateTooltip();
|
||||
}
|
||||
|
||||
public refreshKeybindingTooltip(): void {
|
||||
this._updateTooltip();
|
||||
}
|
||||
|
||||
private _updatePresentation(): void {
|
||||
if (this._expanded) {
|
||||
this.label = localize('hideTerminalOutput', 'Hide Output');
|
||||
this.class = ThemeIcon.asClassName(Codicon.chevronDown);
|
||||
} else {
|
||||
this.label = localize('showTerminalOutput', 'Show Output');
|
||||
this.class = ThemeIcon.asClassName(Codicon.chevronRight);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateTooltip(): void {
|
||||
this.tooltip = this._keybindingService.appendKeybinding(this.label, TerminalContribCommandId.FocusMostRecentChatTerminalOutput);
|
||||
}
|
||||
}
|
||||
|
||||
export class FocusChatInstanceAction extends Action implements IAction {
|
||||
constructor(
|
||||
private _instance: ITerminalInstance | undefined,
|
||||
private _command: ITerminalCommand | undefined,
|
||||
private readonly _commandUri: URI | undefined,
|
||||
private readonly _commandId: string | undefined,
|
||||
isTerminalHidden: boolean,
|
||||
@ITerminalService private readonly _terminalService: ITerminalService,
|
||||
@ITerminalEditorService private readonly _terminalEditorService: ITerminalEditorService,
|
||||
@ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
) {
|
||||
super(
|
||||
TerminalContribCommandId.FocusChatInstanceAction,
|
||||
isTerminalHidden ? localize('showTerminal', 'Show and Focus Terminal') : localize('focusTerminal', 'Focus Terminal'),
|
||||
ThemeIcon.asClassName(Codicon.openInProduct),
|
||||
true,
|
||||
);
|
||||
this._updateTooltip();
|
||||
}
|
||||
|
||||
public override async run() {
|
||||
this.label = this._instance?.shellLaunchConfig.hideFromUser ? localize('showAndFocusTerminal', 'Show and Focus Terminal') : localize('focusTerminal', 'Focus Terminal');
|
||||
this._updateTooltip();
|
||||
|
||||
let target: FocusChatInstanceTelemetryEvent['target'] = 'none';
|
||||
let location: FocusChatInstanceTelemetryEvent['location'] = 'panel';
|
||||
if (this._instance) {
|
||||
target = 'instance';
|
||||
location = this._instance.target === TerminalLocation.Editor ? 'editor' : 'panel';
|
||||
} else if (this._commandUri) {
|
||||
target = 'commandUri';
|
||||
}
|
||||
|
||||
type FocusChatInstanceTelemetryEvent = {
|
||||
target: 'instance' | 'commandUri' | 'none';
|
||||
location: 'panel' | 'editor';
|
||||
};
|
||||
|
||||
type FocusChatInstanceTelemetryClassification = {
|
||||
owner: 'meganrogge';
|
||||
comment: 'Track usage of the focus chat terminal action.';
|
||||
target: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether focusing targeted an existing instance or opened a command URI.' };
|
||||
location: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Location of the terminal instance when focusing.' };
|
||||
};
|
||||
this._telemetryService.publicLog2<FocusChatInstanceTelemetryEvent, FocusChatInstanceTelemetryClassification>('terminal/chatFocusInstance', {
|
||||
target,
|
||||
location
|
||||
});
|
||||
|
||||
if (this._instance) {
|
||||
this._terminalService.setActiveInstance(this._instance);
|
||||
if (this._instance.target === TerminalLocation.Editor) {
|
||||
this._terminalEditorService.openEditor(this._instance);
|
||||
} else {
|
||||
await this._terminalGroupService.showPanel(true);
|
||||
}
|
||||
this._terminalService.setActiveInstance(this._instance);
|
||||
await this._instance.focusWhenReady(true);
|
||||
const command = this._resolveCommand();
|
||||
if (command) {
|
||||
this._instance.xterm?.markTracker.revealCommand(command);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._commandUri) {
|
||||
this._terminalService.openResource(this._commandUri);
|
||||
}
|
||||
}
|
||||
|
||||
public refreshKeybindingTooltip(): void {
|
||||
this._updateTooltip();
|
||||
}
|
||||
|
||||
private _resolveCommand(): ITerminalCommand | undefined {
|
||||
if (this._command && !this._command.endMarker?.isDisposed) {
|
||||
return this._command;
|
||||
}
|
||||
if (!this._instance || !this._commandId) {
|
||||
return this._command;
|
||||
}
|
||||
const commandDetection = this._instance.capabilities.get(TerminalCapability.CommandDetection);
|
||||
const resolved = commandDetection?.commands.find(c => c.id === this._commandId);
|
||||
if (resolved) {
|
||||
this._command = resolved;
|
||||
}
|
||||
return this._command;
|
||||
}
|
||||
|
||||
private _updateTooltip(): void {
|
||||
this.tooltip = this._keybindingService.appendKeybinding(this.label, TerminalContribCommandId.FocusMostRecentChatTerminal);
|
||||
}
|
||||
}
|
||||
|
||||
export class ContinueInBackgroundAction extends Action implements IAction {
|
||||
constructor(
|
||||
private readonly _terminalToolSessionId: string,
|
||||
@ITerminalChatService private readonly _terminalChatService: ITerminalChatService,
|
||||
) {
|
||||
super(
|
||||
TerminalContribCommandId.ContinueInBackground,
|
||||
localize('continueInBackground', 'Continue in Background'),
|
||||
ThemeIcon.asClassName(Codicon.debugContinue),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
public override async run(): Promise<void> {
|
||||
this._terminalChatService.continueInBackground(this._terminalToolSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
export class ChatTerminalThinkingCollapsibleWrapper extends ChatCollapsibleContentPart {
|
||||
private readonly _terminalContentElement: HTMLElement;
|
||||
private readonly _commandText: string;
|
||||
|
||||
@@ -111,6 +111,8 @@ export interface IChatTerminalToolProgressPart {
|
||||
readonly contentIndex: number;
|
||||
focusTerminal(): Promise<void>;
|
||||
toggleOutputFromKeyboard(): Promise<void>;
|
||||
toggleOutputFromAction(): Promise<void>;
|
||||
continueInBackground(): void;
|
||||
focusOutput(): void;
|
||||
getCommandAndOutputAsText(): string | undefined;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// HACK: Export some chat-specific symbols from `terminalContrib/` that are depended upon elsewhere.
|
||||
// These are soft layer breakers between `terminal/` and `terminalContrib/` but there are
|
||||
// difficulties in removing the dependency. These are explicitly defined here to avoid an eslint
|
||||
// line override.
|
||||
export { MENU_CHAT_TERMINAL_TOOL_PROGRESS, TerminalChatContextKeys } from '../terminalContrib/chat/browser/terminalChat.js';
|
||||
@@ -31,6 +31,7 @@ export const enum TerminalChatCommandId {
|
||||
export const MENU_TERMINAL_CHAT_WIDGET_INPUT_SIDE_TOOLBAR = MenuId.for('terminalChatWidget');
|
||||
export const MENU_TERMINAL_CHAT_WIDGET_STATUS = MenuId.for('terminalChatWidget.status');
|
||||
export const MENU_TERMINAL_CHAT_WIDGET_TOOLBAR = MenuId.for('terminalChatWidget.toolbar');
|
||||
export const MENU_CHAT_TERMINAL_TOOL_PROGRESS = MenuId.for('chatTerminalToolProgress');
|
||||
|
||||
export const enum TerminalChatContextKeyStrings {
|
||||
ChatFocus = 'terminalChatFocus',
|
||||
@@ -45,6 +46,12 @@ export const enum TerminalChatContextKeyStrings {
|
||||
ChatSessionResponseVote = 'terminalChatSessionResponseVote',
|
||||
ChatHasTerminals = 'hasChatTerminals',
|
||||
ChatHasHiddenTerminals = 'hasHiddenChatTerminals',
|
||||
ChatToolHasInstance = 'chatTerminalToolHasInstance',
|
||||
ChatToolCanContinueInBackground = 'chatTerminalToolCanContinueInBackground',
|
||||
ChatToolHasOutput = 'chatTerminalToolHasOutput',
|
||||
ChatToolUsesCollapsible = 'chatTerminalToolUsesCollapsible',
|
||||
ChatToolIsHiddenTerminal = 'chatTerminalToolIsHiddenTerminal',
|
||||
ChatToolOutputExpanded = 'chatTerminalToolOutputExpanded',
|
||||
}
|
||||
|
||||
|
||||
@@ -76,4 +83,22 @@ export namespace TerminalChatContextKeys {
|
||||
|
||||
/** Has hidden chat terminals */
|
||||
export const hasHiddenChatTerminals = new RawContextKey<boolean>(TerminalChatContextKeyStrings.ChatHasHiddenTerminals, false, localize('terminalHasHiddenChatTerminals', "Whether there are any hidden chat terminals."));
|
||||
|
||||
/** Whether the per-instance terminal tool progress part has a terminal instance attached */
|
||||
export const chatToolHasInstance = new RawContextKey<boolean>(TerminalChatContextKeyStrings.ChatToolHasInstance, false);
|
||||
|
||||
/** Whether the continue-in-background action is available */
|
||||
export const chatToolCanContinueInBackground = new RawContextKey<boolean>(TerminalChatContextKeyStrings.ChatToolCanContinueInBackground, false);
|
||||
|
||||
/** Whether terminal output is available for display */
|
||||
export const chatToolHasOutput = new RawContextKey<boolean>(TerminalChatContextKeyStrings.ChatToolHasOutput, false);
|
||||
|
||||
/** Whether the terminal tool uses a collapsible wrapper */
|
||||
export const chatToolUsesCollapsible = new RawContextKey<boolean>(TerminalChatContextKeyStrings.ChatToolUsesCollapsible, false);
|
||||
|
||||
/** Whether the associated terminal is hidden from the user */
|
||||
export const chatToolIsHiddenTerminal = new RawContextKey<boolean>(TerminalChatContextKeyStrings.ChatToolIsHiddenTerminal, false);
|
||||
|
||||
/** Whether the terminal output section is currently expanded */
|
||||
export const chatToolOutputExpanded = new RawContextKey<boolean>(TerminalChatContextKeyStrings.ChatToolOutputExpanded, false);
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from '../../../../../base/common/event.js';
|
||||
import { Disposable, DisposableMap, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js';
|
||||
import { Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js';
|
||||
import { ResourceMap } from '../../../../../base/common/map.js';
|
||||
import { URI } from '../../../../../base/common/uri.js';
|
||||
import { ILogService } from '../../../../../platform/log/common/log.js';
|
||||
@@ -86,6 +86,9 @@ export class TerminalChatService extends Disposable implements ITerminalChatServ
|
||||
this._sessionAutoApprovalEnabled.delete(resource);
|
||||
}
|
||||
}));
|
||||
|
||||
// Update context keys when terminal instances change (registered once, not per-registration)
|
||||
this._register(this._terminalService.onDidChangeInstances(() => this._updateHasToolTerminalContextKeys()));
|
||||
}
|
||||
|
||||
registerTerminalInstanceWithToolSession(terminalToolSessionId: string | undefined, instance: ITerminalInstance): void {
|
||||
@@ -96,15 +99,15 @@ export class TerminalChatService extends Disposable implements ITerminalChatServ
|
||||
this._terminalInstancesByToolSessionId.set(terminalToolSessionId, instance);
|
||||
this._toolSessionIdByTerminalInstance.set(instance, terminalToolSessionId);
|
||||
this._onDidRegisterTerminalInstanceForToolSession.fire(instance);
|
||||
this._terminalInstanceListenersByToolSessionId.set(terminalToolSessionId, instance.onDisposed(() => {
|
||||
const instanceStore = new DisposableStore();
|
||||
instanceStore.add(instance.onDisposed(() => {
|
||||
this._terminalInstancesByToolSessionId.delete(terminalToolSessionId);
|
||||
this._toolSessionIdByTerminalInstance.delete(instance);
|
||||
this._terminalInstanceListenersByToolSessionId.deleteAndDispose(terminalToolSessionId);
|
||||
this._persistToStorage();
|
||||
this._updateHasToolTerminalContextKeys();
|
||||
}));
|
||||
|
||||
this._register(this._chatService.onDidDisposeSession(e => {
|
||||
instanceStore.add(this._chatService.onDidDisposeSession(e => {
|
||||
for (const resource of e.sessionResources) {
|
||||
if (LocalChatSessionUri.parseLocalSessionId(resource) === terminalToolSessionId) {
|
||||
this._terminalInstancesByToolSessionId.delete(terminalToolSessionId);
|
||||
@@ -117,9 +120,7 @@ export class TerminalChatService extends Disposable implements ITerminalChatServ
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// Update context keys when terminal instances change (including when terminals are created, disposed, revealed, or hidden)
|
||||
this._register(this._terminalService.onDidChangeInstances(() => this._updateHasToolTerminalContextKeys()));
|
||||
this._terminalInstanceListenersByToolSessionId.set(terminalToolSessionId, instanceStore);
|
||||
|
||||
if (isNumber(instance.shellLaunchConfig?.attachPersistentProcess?.id) || isNumber(instance.persistentProcessId)) {
|
||||
this._persistToStorage();
|
||||
|
||||
+14
-11
@@ -145,6 +145,14 @@ suite('RunInTerminalTool', () => {
|
||||
onDidChangeSessionArchivedState: chatSessionArchivedEmitter.event,
|
||||
} as IAgentSessionsService['model']
|
||||
});
|
||||
instantiationService.stub(ITerminalService, {
|
||||
createTerminal: async () => createdTerminalInstance,
|
||||
onDidDisposeInstance: terminalServiceDisposeEmitter.event,
|
||||
onDidChangeInstances: Event.None,
|
||||
revealTerminal: async () => { },
|
||||
setActiveInstance: () => { },
|
||||
setNextCommandId: async () => { }
|
||||
});
|
||||
instantiationService.stub(ITerminalChatService, store.add(instantiationService.createInstance(TerminalChatService)));
|
||||
instantiationService.stub(IWorkspaceContextService, workspaceContextService);
|
||||
instantiationService.stub(IHistoryService, {
|
||||
@@ -182,13 +190,6 @@ suite('RunInTerminalTool', () => {
|
||||
return [];
|
||||
},
|
||||
});
|
||||
instantiationService.stub(ITerminalService, {
|
||||
createTerminal: async () => createdTerminalInstance,
|
||||
onDidDisposeInstance: terminalServiceDisposeEmitter.event,
|
||||
revealTerminal: async () => { },
|
||||
setActiveInstance: () => { },
|
||||
setNextCommandId: async () => { }
|
||||
});
|
||||
instantiationService.stub(ITerminalProfileResolverService, {
|
||||
getDefaultProfile: async () => ({ path: 'bash' } as ITerminalProfile)
|
||||
});
|
||||
@@ -2075,6 +2076,12 @@ suite('ChatAgentToolsContribution - tool registration refresh', () => {
|
||||
onDidChangeSessionArchivedState: chatSessionArchivedEmitter.event,
|
||||
} as IAgentSessionsService['model']
|
||||
});
|
||||
const terminalInstancesChangedEmitter = store.add(new Emitter<void>());
|
||||
instantiationService.stub(ITerminalService, {
|
||||
onDidDisposeInstance: terminalServiceDisposeEmitter.event,
|
||||
onDidChangeInstances: terminalInstancesChangedEmitter.event,
|
||||
setNextCommandId: async () => { }
|
||||
});
|
||||
instantiationService.stub(ITerminalChatService, store.add(instantiationService.createInstance(TerminalChatService)));
|
||||
instantiationService.stub(IHistoryService, {
|
||||
getLastActiveWorkspaceRoot: () => undefined
|
||||
@@ -2102,10 +2109,6 @@ suite('ChatAgentToolsContribution - tool registration refresh', () => {
|
||||
treeSitterLibraryService.isTest = true;
|
||||
instantiationService.stub(ITreeSitterLibraryService, treeSitterLibraryService);
|
||||
|
||||
instantiationService.stub(ITerminalService, {
|
||||
onDidDisposeInstance: terminalServiceDisposeEmitter.event,
|
||||
setNextCommandId: async () => { }
|
||||
});
|
||||
instantiationService.stub(ITerminalProfileResolverService, {
|
||||
getDefaultProfile: async () => ({ path: 'bash' } as ITerminalProfile)
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user