mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-15 07:28:05 +00:00
ensure command id defined for non copilot terminals (#274954)
This commit is contained in:
@@ -276,12 +276,6 @@ export interface IHandleCommandOptions {
|
|||||||
* Properties for the mark
|
* Properties for the mark
|
||||||
*/
|
*/
|
||||||
markProperties?: IMarkProperties;
|
markProperties?: IMarkProperties;
|
||||||
|
|
||||||
/**
|
|
||||||
* An optional predefined command ID. When provided, this ID will be used instead of
|
|
||||||
* generating a new one, allowing commands to be linked across renderer and ptyHost.
|
|
||||||
*/
|
|
||||||
commandId?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INaiveCwdDetectionCapability {
|
export interface INaiveCwdDetectionCapability {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import { IMarkProperties, ISerializedTerminalCommand, ITerminalCommand } from '../capabilities.js';
|
import { IMarkProperties, ISerializedTerminalCommand, ITerminalCommand } from '../capabilities.js';
|
||||||
import { ITerminalOutputMatcher, ITerminalOutputMatch } from '../../terminal.js';
|
import { ITerminalOutputMatcher, ITerminalOutputMatch } from '../../terminal.js';
|
||||||
import type { IBuffer, IBufferLine, IMarker, Terminal } from '@xterm/headless';
|
import type { IBuffer, IBufferLine, IMarker, Terminal } from '@xterm/headless';
|
||||||
|
import { generateUuid } from '../../../../../base/common/uuid.js';
|
||||||
|
|
||||||
export interface ITerminalCommandProperties {
|
export interface ITerminalCommandProperties {
|
||||||
command: string;
|
command: string;
|
||||||
@@ -284,7 +285,7 @@ export class PartialTerminalCommand implements ICurrentPartialCommand {
|
|||||||
private readonly _xterm: Terminal,
|
private readonly _xterm: Terminal,
|
||||||
id?: string
|
id?: string
|
||||||
) {
|
) {
|
||||||
this.id = id;
|
this.id = id ?? generateUuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(cwd: string | undefined): ISerializedTerminalCommand | undefined {
|
serialize(cwd: string | undefined): ISerializedTerminalCommand | undefined {
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import { RunOnceScheduler } from '../../../../base/common/async.js';
|
|||||||
import { debounce } from '../../../../base/common/decorators.js';
|
import { debounce } from '../../../../base/common/decorators.js';
|
||||||
import { Emitter } from '../../../../base/common/event.js';
|
import { Emitter } from '../../../../base/common/event.js';
|
||||||
import { Disposable, MandatoryMutableDisposable, MutableDisposable } from '../../../../base/common/lifecycle.js';
|
import { Disposable, MandatoryMutableDisposable, MutableDisposable } from '../../../../base/common/lifecycle.js';
|
||||||
|
import { generateUuid } from '../../../../base/common/uuid.js';
|
||||||
import { ILogService } from '../../../log/common/log.js';
|
import { ILogService } from '../../../log/common/log.js';
|
||||||
import { CommandInvalidationReason, ICommandDetectionCapability, ICommandInvalidationRequest, IHandleCommandOptions, ISerializedCommandDetectionCapability, ISerializedTerminalCommand, ITerminalCommand, TerminalCapability } from './capabilities.js';
|
import { CommandInvalidationReason, ICommandDetectionCapability, ICommandInvalidationRequest, IHandleCommandOptions, ISerializedCommandDetectionCapability, ISerializedTerminalCommand, ITerminalCommand, TerminalCapability } from './capabilities.js';
|
||||||
import { ITerminalOutputMatcher } from '../terminal.js';
|
import { ITerminalOutputMatcher } from '../terminal.js';
|
||||||
import { ICurrentPartialCommand, PartialTerminalCommand, TerminalCommand } from './commandDetection/terminalCommand.js';
|
import { ICurrentPartialCommand, PartialTerminalCommand, TerminalCommand } from './commandDetection/terminalCommand.js';
|
||||||
import { PromptInputModel, type IPromptInputModel } from './commandDetection/promptInputModel.js';
|
import { PromptInputModel, type IPromptInputModel } from './commandDetection/promptInputModel.js';
|
||||||
import type { IBuffer, IDisposable, IMarker, Terminal } from '@xterm/headless';
|
import type { IBuffer, IDisposable, IMarker, Terminal } from '@xterm/headless';
|
||||||
import { isString } from '../../../../base/common/types.js';
|
|
||||||
|
|
||||||
interface ITerminalDimensions {
|
interface ITerminalDimensions {
|
||||||
cols: number;
|
cols: number;
|
||||||
@@ -365,20 +365,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe
|
|||||||
if (!this._currentCommand.commandExecutedMarker) {
|
if (!this._currentCommand.commandExecutedMarker) {
|
||||||
this.handleCommandExecuted();
|
this.handleCommandExecuted();
|
||||||
}
|
}
|
||||||
// If a custom command ID is provided, use it for the current command
|
this._ensureCurrentCommandId(this._currentCommand.command ?? this._currentCommand.extractCommandLine());
|
||||||
// Otherwise, check if there's a pending next command ID
|
|
||||||
if (options?.commandId) {
|
|
||||||
this._currentCommand.id = options.commandId;
|
|
||||||
this._nextCommandId = undefined; // Clear the pending ID
|
|
||||||
} else if (
|
|
||||||
this._nextCommandId &&
|
|
||||||
isString(this.currentCommand.command) &&
|
|
||||||
isString(this._nextCommandId.command) &&
|
|
||||||
this.currentCommand.command.trim() === this._nextCommandId.command.trim()
|
|
||||||
) {
|
|
||||||
this._currentCommand.id = this._nextCommandId.commandId;
|
|
||||||
this._nextCommandId = undefined; // Clear after use
|
|
||||||
}
|
|
||||||
this._currentCommand.markFinishedTime();
|
this._currentCommand.markFinishedTime();
|
||||||
this._ptyHeuristics.value.preHandleCommandFinished?.();
|
this._ptyHeuristics.value.preHandleCommandFinished?.();
|
||||||
|
|
||||||
@@ -415,12 +402,25 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe
|
|||||||
this._logService.debug('CommandDetectionCapability#onCommandFinished', newCommand);
|
this._logService.debug('CommandDetectionCapability#onCommandFinished', newCommand);
|
||||||
this._onCommandFinished.fire(newCommand);
|
this._onCommandFinished.fire(newCommand);
|
||||||
}
|
}
|
||||||
// Create new command for next execution, preserving command ID if one was specified
|
// Create new command for next execution
|
||||||
const nextCommandId = this._handleCommandStartOptions?.commandId;
|
this._currentCommand = new PartialTerminalCommand(this._terminal);
|
||||||
this._currentCommand = new PartialTerminalCommand(this._terminal, nextCommandId);
|
|
||||||
this._handleCommandStartOptions = undefined;
|
this._handleCommandStartOptions = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _ensureCurrentCommandId(commandLine: string | undefined): void {
|
||||||
|
if (this._nextCommandId?.commandId && typeof commandLine === 'string' && commandLine.trim() === this._nextCommandId.command.trim()) {
|
||||||
|
if (this._currentCommand.id !== this._nextCommandId.commandId) {
|
||||||
|
this._currentCommand.id = this._nextCommandId.commandId;
|
||||||
|
}
|
||||||
|
this._nextCommandId = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._currentCommand.id) {
|
||||||
|
this._currentCommand.id = generateUuid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setCommandLine(commandLine: string, isTrusted: boolean) {
|
setCommandLine(commandLine: string, isTrusted: boolean) {
|
||||||
this._logService.debug('CommandDetectionCapability#setCommandLine', commandLine, isTrusted);
|
this._logService.debug('CommandDetectionCapability#setCommandLine', commandLine, isTrusted);
|
||||||
this._currentCommand.command = commandLine;
|
this._currentCommand.command = commandLine;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com
|
|||||||
import { TerminalContext } from '../../../chat/browser/actions/chatContext.js';
|
import { TerminalContext } from '../../../chat/browser/actions/chatContext.js';
|
||||||
import { getTerminalUri, parseTerminalUri } from '../terminalUri.js';
|
import { getTerminalUri, parseTerminalUri } from '../terminalUri.js';
|
||||||
import { URI } from '../../../../../base/common/uri.js';
|
import { URI } from '../../../../../base/common/uri.js';
|
||||||
|
import { ChatAgentLocation } from '../../../chat/common/constants.js';
|
||||||
|
|
||||||
interface IDisposableDecoration { decoration: IDecoration; disposables: IDisposable[]; exitCode?: number; markProperties?: IMarkProperties }
|
interface IDisposableDecoration { decoration: IDecoration; disposables: IDisposable[]; exitCode?: number; markProperties?: IMarkProperties }
|
||||||
|
|
||||||
@@ -526,12 +527,13 @@ export class DecorationAddon extends Disposable implements ITerminalAddon, IDeco
|
|||||||
return {
|
return {
|
||||||
class: undefined, tooltip: labelAttachToChat, id: 'terminal.attachToChat', label: labelAttachToChat, enabled: true,
|
class: undefined, tooltip: labelAttachToChat, id: 'terminal.attachToChat', label: labelAttachToChat, enabled: true,
|
||||||
run: async () => {
|
run: async () => {
|
||||||
const widget = this._chatWidgetService.lastFocusedWidget;
|
const widget = this._chatWidgetService.lastFocusedWidget ?? this._chatWidgetService.getWidgetsByLocations(ChatAgentLocation.Chat)?.[0];
|
||||||
let terminalContext: TerminalContext | undefined;
|
let terminalContext: TerminalContext | undefined;
|
||||||
if (this._resource) {
|
if (this._resource) {
|
||||||
const parsedUri = parseTerminalUri(this._resource);
|
const parsedUri = parseTerminalUri(this._resource);
|
||||||
terminalContext = this._instantiationService.createInstance(TerminalContext, getTerminalUri(parsedUri.workspaceId, parsedUri.instanceId!, undefined, command.id));
|
terminalContext = this._instantiationService.createInstance(TerminalContext, getTerminalUri(parsedUri.workspaceId, parsedUri.instanceId!, undefined, command.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (terminalContext && widget && widget.attachmentCapabilities.supportsTerminalAttachments) {
|
if (terminalContext && widget && widget.attachmentCapabilities.supportsTerminalAttachments) {
|
||||||
try {
|
try {
|
||||||
const attachment = await terminalContext.asAttachment(widget);
|
const attachment = await terminalContext.asAttachment(widget);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ suite('CommandDetectionCapability', () => {
|
|||||||
// Ensure timestamps are set and were captured recently
|
// Ensure timestamps are set and were captured recently
|
||||||
for (const command of capability.commands) {
|
for (const command of capability.commands) {
|
||||||
ok(Math.abs(Date.now() - command.timestamp) < 2000);
|
ok(Math.abs(Date.now() - command.timestamp) < 2000);
|
||||||
|
ok(command.id, 'Expected command to have an assigned id');
|
||||||
}
|
}
|
||||||
deepStrictEqual(addEvents, capability.commands);
|
deepStrictEqual(addEvents, capability.commands);
|
||||||
// Clear the commands to avoid re-asserting past commands
|
// Clear the commands to avoid re-asserting past commands
|
||||||
|
|||||||
Reference in New Issue
Block a user