Не подтверждена Коммит 5330d51b создал по автору meganrogge's avatar meganrogge
Просмотр файлов

extract terminal command and output collection from RunInTerminalTool

владелец 3dfe27c9
......@@ -15,7 +15,6 @@ import { basename } from '../../../../../../base/common/path.js';
import { OperatingSystem, OS } from '../../../../../../base/common/platform.js';
import { count } from '../../../../../../base/common/strings.js';
import { generateUuid } from '../../../../../../base/common/uuid.js';
import { URI } from '../../../../../../base/common/uri.js';
import { localize } from '../../../../../../nls.js';
import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js';
import { IInstantiationService, type ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation.js';
......@@ -46,12 +45,12 @@ import { CommandLineFileWriteAnalyzer } from './commandLineAnalyzer/commandLineF
import { OutputMonitor } from './monitoring/outputMonitor.js';
import { IPollingResult, OutputMonitorState } from './monitoring/types.js';
import { LocalChatSessionUri } from '../../../../chat/common/chatUri.js';
import { CHAT_TERMINAL_OUTPUT_MAX_PREVIEW_LINES } from '../../../../chat/common/constants.js';
import type { ICommandLineRewriter } from './commandLineRewriter/commandLineRewriter.js';
import { CommandLineCdPrefixRewriter } from './commandLineRewriter/commandLineCdPrefixRewriter.js';
import { CommandLinePwshChainOperatorRewriter } from './commandLineRewriter/commandLinePwshChainOperatorRewriter.js';
import { IWorkspaceContextService } from '../../../../../../platform/workspace/common/workspace.js';
import { IHistoryService } from '../../../../../services/history/common/history.js';
import { TerminalCommandArtifactCollector } from './terminalCommandArtifactCollector.js';
// #region Tool data
......@@ -256,6 +255,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
private readonly _terminalToolCreator: ToolTerminalCreator;
private readonly _treeSitterCommandParser: TreeSitterCommandParser;
private readonly _telemetry: RunInTerminalToolTelemetry;
private readonly _commandArtifactCollector: TerminalCommandArtifactCollector;
protected readonly _profileFetcher: TerminalProfileFetcher;
private readonly _commandLineRewriters: ICommandLineRewriter[];
......@@ -295,6 +295,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
this._terminalToolCreator = this._instantiationService.createInstance(ToolTerminalCreator);
this._treeSitterCommandParser = this._register(this._instantiationService.createInstance(TreeSitterCommandParser));
this._telemetry = this._instantiationService.createInstance(RunInTerminalToolTelemetry);
this._commandArtifactCollector = this._instantiationService.createInstance(TerminalCommandArtifactCollector);
this._profileFetcher = this._instantiationService.createInstance(TerminalProfileFetcher);
this._commandLineRewriters = [
......@@ -542,7 +543,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
throw new CancellationError();
}
await this._captureCommandArtifacts(toolSpecificData, toolTerminal.instance, commandId, pollingResult?.output);
await this._commandArtifactCollector.capture(toolSpecificData, toolTerminal.instance, commandId, pollingResult?.output);
let resultText = (
didUserEditCommand
......@@ -638,7 +639,7 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
throw new CancellationError();
}
await this._captureCommandArtifacts(toolSpecificData, toolTerminal.instance, commandId, executeResult.output);
await this._commandArtifactCollector.capture(toolSpecificData, toolTerminal.instance, commandId, executeResult.output);
this._logService.debug(`RunInTerminalTool: Finished \`${strategy.type}\` execute strategy with exitCode \`${executeResult.exitCode}\`, result.length \`${executeResult.output?.length}\`, error \`${executeResult.error}\``);
outputLineCount = executeResult.output === undefined ? 0 : count(executeResult.output.trim(), '\n') + 1;
......@@ -767,69 +768,6 @@ export class RunInTerminalTool extends Disposable implements IToolImpl {
}
// #endregion
// #region Command serialization
private _createTerminalCommandUri(instance: ITerminalInstance, commandId: string): URI {
const params = new URLSearchParams(instance.resource.query);
params.set('command', commandId);
return instance.resource.with({ query: params.toString() });
}
private async _captureCommandArtifacts(
toolSpecificData: IChatTerminalToolInvocationData,
instance: ITerminalInstance,
commandId: string | undefined,
fallbackOutput?: string
): Promise<void> {
if (commandId) {
try {
toolSpecificData.terminalCommandUri = this._createTerminalCommandUri(instance, commandId);
} catch (error) {
this._logService.warn(`RunInTerminalTool: Failed to create terminal command URI for ${commandId}`, error);
}
const serialized = await this._tryGetSerializedCommandOutput(instance, commandId);
if (serialized) {
toolSpecificData.terminalCommandOutput = { text: serialized.text, truncated: serialized.truncated };
const theme = instance.xterm?.getXtermTheme();
if (theme) {
toolSpecificData.terminalTheme = { background: theme.background, foreground: theme.foreground };
}
return;
}
}
if (fallbackOutput !== undefined) {
const normalized = fallbackOutput.replace(/\r\n/g, '\n');
toolSpecificData.terminalCommandOutput = { text: normalized, truncated: false };
const theme = instance.xterm?.getXtermTheme();
if (theme) {
toolSpecificData.terminalTheme = { background: theme.background, foreground: theme.foreground };
}
}
}
private async _tryGetSerializedCommandOutput(instance: ITerminalInstance, commandId: string): Promise<{ text: string; truncated?: boolean } | undefined> {
const commandDetection = instance.capabilities.get(TerminalCapability.CommandDetection);
const command = commandDetection?.commands.find(c => c.id === commandId);
if (!command?.endMarker) {
return undefined;
}
const xterm = await instance.xtermReadyPromise;
if (!xterm) {
return undefined;
}
try {
return await xterm.getCommandOutputAsHtml(command, CHAT_TERMINAL_OUTPUT_MAX_PREVIEW_LINES);
} catch (error) {
this._logService.warn(`RunInTerminalTool: Failed to serialize command output for ${commandId}`, error);
return undefined;
}
}
// #endregion
// #region Session management
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from '../../../../../../base/common/uri.js';
import { IChatTerminalToolInvocationData } from '../../../../chat/common/chatService.js';
import { CHAT_TERMINAL_OUTPUT_MAX_PREVIEW_LINES } from '../../../../chat/common/constants.js';
import { ITerminalInstance } from '../../../../terminal/browser/terminal.js';
import { TerminalCapability } from '../../../../../../platform/terminal/common/capabilities/capabilities.js';
import { ITerminalLogService } from '../../../../../../platform/terminal/common/terminal.js';
export class TerminalCommandArtifactCollector {
constructor(
@ITerminalLogService private readonly _logService: ITerminalLogService,
) { }
async capture(
toolSpecificData: IChatTerminalToolInvocationData,
instance: ITerminalInstance,
commandId: string | undefined,
fallbackOutput?: string
): Promise<void> {
if (commandId) {
try {
toolSpecificData.terminalCommandUri = this._createTerminalCommandUri(instance, commandId);
} catch (error) {
this._logService.warn(`RunInTerminalTool: Failed to create terminal command URI for ${commandId}`, error);
}
const serialized = await this._tryGetSerializedCommandOutput(instance, commandId);
if (serialized) {
toolSpecificData.terminalCommandOutput = { text: serialized.text, truncated: serialized.truncated };
this._applyTheme(toolSpecificData, instance);
return;
}
}
if (fallbackOutput !== undefined) {
const normalized = fallbackOutput.replace(/\r\n/g, '\n');
toolSpecificData.terminalCommandOutput = { text: normalized, truncated: false };
this._applyTheme(toolSpecificData, instance);
}
}
private _applyTheme(toolSpecificData: IChatTerminalToolInvocationData, instance: ITerminalInstance): void {
const theme = instance.xterm?.getXtermTheme();
if (theme) {
toolSpecificData.terminalTheme = { background: theme.background, foreground: theme.foreground };
}
}
private _createTerminalCommandUri(instance: ITerminalInstance, commandId: string): URI {
const params = new URLSearchParams(instance.resource.query);
params.set('command', commandId);
return instance.resource.with({ query: params.toString() });
}
private async _tryGetSerializedCommandOutput(instance: ITerminalInstance, commandId: string): Promise<{ text: string; truncated?: boolean } | undefined> {
const commandDetection = instance.capabilities.get(TerminalCapability.CommandDetection);
const command = commandDetection?.commands.find(c => c.id === commandId);
if (!command?.endMarker) {
return undefined;
}
const xterm = await instance.xtermReadyPromise;
if (!xterm) {
return undefined;
}
try {
return await xterm.getCommandOutputAsHtml(command, CHAT_TERMINAL_OUTPUT_MAX_PREVIEW_LINES);
} catch (error) {
this._logService.warn(`RunInTerminalTool: Failed to serialize command output for ${commandId}`, error);
return undefined;
}
}
}
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать