Move partial command to a class

This commit is contained in:
Daniel Imms 2023-11-13 07:49:20 -08:00
parent 2652627a56
commit d597949b55
No known key found for this signature in database
GPG key ID: E5CF412B63651C69
2 changed files with 97 additions and 55 deletions

View file

@ -9,6 +9,7 @@ import { ITerminalOutputMatcher, ITerminalOutputMatch } from 'vs/platform/termin
// Importing types is safe in any layer
// eslint-disable-next-line local/code-import-patterns
import type { IBuffer, IBufferLine, Terminal } from '@xterm/headless';
import { ICurrentPartialCommand } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability';
export interface ITerminalCommandProperties {
command: string;
@ -190,6 +191,88 @@ export class TerminalCommand implements ITerminalCommand {
}
}
export class PartialTerminalCommand implements ICurrentPartialCommand {
previousCommandMarker?: IMarker;
promptStartMarker?: IMarker;
commandStartMarker?: IMarker;
commandStartX?: number;
commandStartLineContent?: string;
commandRightPromptStartX?: number;
commandRightPromptEndX?: number;
commandLines?: IMarker;
commandExecutedMarker?: IMarker;
commandExecutedX?: number;
commandFinishedMarker?: IMarker;
currentContinuationMarker?: IMarker;
continuations?: { marker: IMarker; end: number }[];
command?: string;
isTrusted?: boolean;
isInvalid?: boolean;
constructor(
private readonly _xterm: Terminal,
) {
}
serialize(cwd: string | undefined): ISerializedTerminalCommand | undefined {
if (!this.commandStartMarker) {
return undefined;
}
return {
promptStartLine: this.promptStartMarker?.line,
startLine: this.commandStartMarker.line,
startX: this.commandStartX,
endLine: undefined,
executedLine: undefined,
executedX: undefined,
command: '',
isTrusted: true,
cwd,
exitCode: undefined,
commandStartLineContent: undefined,
timestamp: 0,
markProperties: undefined
};
}
promoteToFullCommand(cwd: string | undefined, exitCode: number | undefined, ignoreCommandLine: boolean, markProperties: IMarkProperties | undefined): TerminalCommand | undefined {
// When the command finishes and executed never fires the placeholder selector should be used.
if (exitCode === undefined && this.command === undefined) {
this.command = '';
}
if ((this.command !== undefined && !this.command.startsWith('\\')) || ignoreCommandLine) {
return new TerminalCommand(this._xterm, {
command: ignoreCommandLine ? '' : (this.command || ''),
isTrusted: !!this.isTrusted,
promptStartMarker: this.promptStartMarker,
marker: this.commandStartMarker,
startX: this.commandStartX,
endMarker: this.commandFinishedMarker,
executedMarker: this.commandExecutedMarker,
executedX: this.commandExecutedX,
timestamp: Date.now(),
cwd,
exitCode,
commandStartLineContent: this.commandStartLineContent,
markProperties
});
}
return undefined;
}
}
function getXtermLineContent(buffer: IBuffer, lineStart: number, lineEnd: number, cols: number): string {
// Cap the maximum number of lines generated to prevent potential performance problems. This is
// more of a sanity check as the wrapped line should already be trimmed down at this point.

View file

@ -14,7 +14,7 @@ import { ITerminalOutputMatcher } from 'vs/platform/terminal/common/terminal';
// Importing types is safe in any layer
// eslint-disable-next-line local/code-import-patterns
import type { IBuffer, IDisposable, IMarker, Terminal } from '@xterm/headless';
import { TerminalCommand } from 'vs/platform/terminal/common/capabilities/commandDetection/terminalCommand';
import { PartialTerminalCommand, TerminalCommand } from 'vs/platform/terminal/common/capabilities/commandDetection/terminalCommand';
export interface ICurrentPartialCommand {
previousCommandMarker?: IMarker;
@ -61,9 +61,8 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe
readonly type = TerminalCapability.CommandDetection;
protected _commands: TerminalCommand[] = [];
private _exitCode: number | undefined;
private _cwd: string | undefined;
private _currentCommand: ICurrentPartialCommand = {};
private _currentCommand: PartialTerminalCommand = new PartialTerminalCommand(this._terminal);
private _commandMarkers: IMarker[] = [];
private _dimensions: ITerminalDimensions;
private __isCommandStorageDisabled: boolean = false;
@ -325,20 +324,17 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe
handleCommandFinished(exitCode: number | undefined, options?: IHandleCommandOptions): void {
this._ptyHeuristics.value?.preHandleCommandFinished?.();
this._currentCommand.commandFinishedMarker = options?.marker || this._terminal.registerMarker(0);
let command = this._currentCommand.command;
this._logService.debug('CommandDetectionCapability#handleCommandFinished', this._terminal.buffer.active.cursorX, this._currentCommand.commandFinishedMarker?.line, this._currentCommand.command, this._currentCommand);
this._exitCode = exitCode;
this._logService.debug('CommandDetectionCapability#handleCommandFinished', this._terminal.buffer.active.cursorX, options?.marker?.line, this._currentCommand.command, this._currentCommand);
// HACK: Handle a special case on some versions of bash where identical commands get merged
// in the output of `history`, this detects that case and sets the exit code to the the last
// command's exit code. This covered the majority of cases but will fail if the same command
// runs with a different exit code, that will need a more robust fix where we send the
// command ID and exit code over to the capability to adjust there.
if (this._exitCode === undefined) {
if (exitCode === undefined) {
const lastCommand = this.commands.length > 0 ? this.commands[this.commands.length - 1] : undefined;
if (command && command.length > 0 && lastCommand?.command === command) {
this._exitCode = lastCommand.exitCode;
if (this._currentCommand.command && this._currentCommand.command.length > 0 && lastCommand?.command === this._currentCommand.command) {
exitCode = lastCommand.exitCode;
}
}
@ -346,32 +342,12 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe
return;
}
// When the command finishes and executed never fires the placeholder selector should be used.
if (this._exitCode === undefined && command === undefined) {
command = '';
}
this._ptyHeuristics.value?.postHandleCommandFinished?.();
if ((command !== undefined && !command.startsWith('\\')) || this._handleCommandStartOptions?.ignoreCommandLine) {
const timestamp = Date.now();
const executedMarker = this._currentCommand.commandExecutedMarker;
const endMarker = this._currentCommand.commandFinishedMarker;
const newCommand = new TerminalCommand(this._terminal, {
command: this._handleCommandStartOptions?.ignoreCommandLine ? '' : (command || ''),
isTrusted: !!this._currentCommand.isTrusted,
promptStartMarker: this._currentCommand.promptStartMarker,
marker: this._currentCommand.commandStartMarker,
startX: this._currentCommand.commandStartX,
endMarker,
executedMarker,
executedX: this._currentCommand.commandExecutedX,
timestamp,
cwd: this._cwd,
exitCode: this._exitCode,
commandStartLineContent: this._currentCommand.commandStartLineContent,
markProperties: options?.markProperties
});
this._currentCommand.commandFinishedMarker = options?.marker || this._terminal.registerMarker(0);
const newCommand = this._currentCommand.promoteToFullCommand(this._cwd, exitCode, this._handleCommandStartOptions?.ignoreCommandLine ?? false, options?.markProperties);
if (newCommand) {
this._commands.push(newCommand);
this._logService.debug('CommandDetectionCapability#onCommandFinished', newCommand);
@ -381,7 +357,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe
}
}
this._currentCommand.previousCommandMarker = this._currentCommand.commandStartMarker;
this._currentCommand = {};
this._currentCommand = new PartialTerminalCommand(this._terminal);
this._handleCommandStartOptions = undefined;
}
@ -392,28 +368,11 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe
}
serialize(): ISerializedCommandDetectionCapability {
// Full commands
const commands: ISerializedTerminalCommand[] = this.commands.map(e => e.serialize(this.__isCommandStorageDisabled));
// Partial command
if (this._currentCommand.commandStartMarker) {
commands.push({
promptStartLine: this._currentCommand.promptStartMarker?.line,
startLine: this._currentCommand.commandStartMarker.line,
startX: this._currentCommand.commandStartX,
endLine: undefined,
executedLine: undefined,
executedX: undefined,
command: '',
isTrusted: true,
cwd: this._cwd,
exitCode: undefined,
commandStartLineContent: undefined,
timestamp: 0,
markProperties: undefined
});
const partialCommand = this._currentCommand.serialize(this._cwd);
if (partialCommand) {
commands.push(partialCommand);
}
return {
isWindowsPty: this._ptyHeuristics.value instanceof WindowsPtyHeuristics,
commands