mirror of
https://github.com/Microsoft/vscode
synced 2024-09-19 18:48:00 +00:00
move invalidation to the commandDetectionCapability (#146563)
This commit is contained in:
parent
2dff5deef7
commit
7e0ee0d395
|
@ -92,6 +92,7 @@ export interface ICommandDetectionCapability {
|
|||
readonly cwd: string | undefined;
|
||||
readonly onCommandStarted: Event<ITerminalCommand>;
|
||||
readonly onCommandFinished: Event<ITerminalCommand>;
|
||||
readonly onCommandInvalidated: Event<ITerminalCommand[]>;
|
||||
setCwd(value: string): void;
|
||||
setIsWindowsPty(value: boolean): void;
|
||||
/**
|
||||
|
|
|
@ -42,6 +42,11 @@ interface ITerminalDimensions {
|
|||
rows: number;
|
||||
}
|
||||
|
||||
interface IBeforeCommandFinishedEvent {
|
||||
command: ITerminalCommand;
|
||||
veto?: boolean;
|
||||
}
|
||||
|
||||
export class CommandDetectionCapability implements ICommandDetectionCapability {
|
||||
readonly type = TerminalCapability.CommandDetection;
|
||||
|
||||
|
@ -67,8 +72,12 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
|
|||
|
||||
private readonly _onCommandStarted = new Emitter<ITerminalCommand>();
|
||||
readonly onCommandStarted = this._onCommandStarted.event;
|
||||
private readonly _onBeforeCommandFinished = new Emitter<IBeforeCommandFinishedEvent>();
|
||||
readonly onBeforeCommandFinished = this._onBeforeCommandFinished.event;
|
||||
private readonly _onCommandFinished = new Emitter<ITerminalCommand>();
|
||||
readonly onCommandFinished = this._onCommandFinished.event;
|
||||
private readonly _onCommandInvalidated = new Emitter<ITerminalCommand[]>();
|
||||
readonly onCommandInvalidated = this._onCommandInvalidated.event;
|
||||
|
||||
constructor(
|
||||
private readonly _terminal: Terminal,
|
||||
|
@ -79,6 +88,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
|
|||
rows: this._terminal.rows
|
||||
};
|
||||
this._terminal.onResize(e => this._handleResize(e));
|
||||
this._setupClearListeners();
|
||||
}
|
||||
|
||||
private _handleResize(e: { cols: number; rows: number }) {
|
||||
|
@ -89,6 +99,36 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
|
|||
this._dimensions.rows = e.rows;
|
||||
}
|
||||
|
||||
private _setupClearListeners() {
|
||||
// Setup listeners for when clear is run in the shell. Since we don't know immediately if
|
||||
// this is a Windows pty, listen to both routes and do the Windows check inside them
|
||||
|
||||
// For a Windows backend we cannot listen to CSI J, instead we assume running clear or
|
||||
// cls will clear all commands in the viewport. This is not perfect but it's right most
|
||||
// of the time.
|
||||
this.onBeforeCommandFinished(event => {
|
||||
if (this._isWindowsPty) {
|
||||
if (event.command.command.trim().toLowerCase() === 'clear' || event.command.command.trim().toLowerCase() === 'cls') {
|
||||
this._clearCommandsInViewport();
|
||||
// Prevent current command to get to command finished listeners
|
||||
event.veto = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// For non-Windows backends we can just listen to CSI J which is what the clear command
|
||||
// typically emits.
|
||||
this._terminal.parser.registerCsiHandler({ final: 'J' }, params => {
|
||||
if (!this._isWindowsPty) {
|
||||
if (params.length >= 1 && (params[0] === 2 || params[0] === 3)) {
|
||||
this._clearCommandsInViewport();
|
||||
}
|
||||
}
|
||||
// We don't want to override xterm.js' default behavior, just augment it
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private _preHandleResizeWindows(e: { cols: number; rows: number }) {
|
||||
// Resize behavior is different under conpty; instead of bringing parts of the scrollback
|
||||
// back into the viewport, new lines are inserted at the bottom (ie. the same behavior as if
|
||||
|
@ -138,6 +178,22 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
|
|||
}
|
||||
}
|
||||
|
||||
private _clearCommandsInViewport(): void {
|
||||
// Find the number of commands on the tail end of the array that are within the viewport
|
||||
let count = 0;
|
||||
for (let i = this._commands.length - 1; i >= 0; i--) {
|
||||
const line = this._commands[i].marker?.line;
|
||||
if (line && line < this._terminal.buffer.active.baseY) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
// Remove them
|
||||
if (count > 0) {
|
||||
this._onCommandInvalidated.fire(this._commands.splice(this._commands.length - count, count));
|
||||
}
|
||||
}
|
||||
|
||||
private _waitForCursorMove(): Promise<void> {
|
||||
const cursorX = this._terminal.buffer.active.cursorX;
|
||||
const cursorY = this._terminal.buffer.active.cursorY;
|
||||
|
@ -346,7 +402,13 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
|
|||
};
|
||||
this._commands.push(newCommand);
|
||||
this._logService.debug('CommandDetectionCapability#onCommandFinished', newCommand);
|
||||
this._onCommandFinished.fire(newCommand);
|
||||
|
||||
// Fire the command finished event provided there is no veto
|
||||
const beforeEvent: IBeforeCommandFinishedEvent = { command: newCommand };
|
||||
this._onBeforeCommandFinished.fire(beforeEvent);
|
||||
if (!beforeEvent.veto) {
|
||||
this._onCommandFinished.fire(newCommand);
|
||||
}
|
||||
}
|
||||
this._currentCommand.previousCommandMarker = this._currentCommand.commandStartMarker;
|
||||
this._currentCommand = {};
|
||||
|
|
|
@ -902,11 +902,6 @@ export interface IXtermTerminal {
|
|||
*/
|
||||
clearBuffer(): void;
|
||||
|
||||
/**
|
||||
* Clears decorations - for example, when shell integration is disabled.
|
||||
*/
|
||||
clearDecorations(): void;
|
||||
|
||||
/**
|
||||
* Clears the search result decorations
|
||||
*/
|
||||
|
|
|
@ -46,6 +46,7 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
|
|||
private _hoverDelayer: Delayer<void>;
|
||||
private _commandStartedListener: IDisposable | undefined;
|
||||
private _commandFinishedListener: IDisposable | undefined;
|
||||
private _commandClearedListener: IDisposable | undefined;
|
||||
private _contextMenuVisible: boolean = false;
|
||||
private _decorations: Map<number, IDisposableDecoration> = new Map();
|
||||
private _placeholderDecoration: IDecoration | undefined;
|
||||
|
@ -63,7 +64,7 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
|
|||
@IOpenerService private readonly _openerService: IOpenerService
|
||||
) {
|
||||
super();
|
||||
this._register(toDisposable(() => this.clearDecorations(true)));
|
||||
this._register(toDisposable(() => this._dispose()));
|
||||
this._register(this._contextMenuService.onDidShowContextMenu(() => this._contextMenuVisible = true));
|
||||
this._register(this._contextMenuService.onDidHideContextMenu(() => this._contextMenuVisible = false));
|
||||
this._hoverDelayer = this._register(new Delayer(this._configurationService.getValue('workbench.hover.delay')));
|
||||
|
@ -111,11 +112,10 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
|
|||
}
|
||||
}
|
||||
|
||||
public clearDecorations(disableDecorations?: boolean): void {
|
||||
if (disableDecorations) {
|
||||
this._commandStartedListener?.dispose();
|
||||
this._commandFinishedListener?.dispose();
|
||||
}
|
||||
private _dispose(): void {
|
||||
this._commandStartedListener?.dispose();
|
||||
this._commandFinishedListener?.dispose();
|
||||
this._commandClearedListener?.dispose();
|
||||
this._placeholderDecoration?.dispose();
|
||||
this._placeholderDecoration?.marker.dispose();
|
||||
for (const value of this._decorations.values()) {
|
||||
|
@ -129,11 +129,13 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
|
|||
if (this._capabilities.has(TerminalCapability.CommandDetection)) {
|
||||
this._addCommandFinishedListener();
|
||||
this._addCommandStartedListener();
|
||||
this._addCommandClearedListener();
|
||||
} else {
|
||||
this._register(this._capabilities.onDidAddCapability(c => {
|
||||
if (c === TerminalCapability.CommandDetection) {
|
||||
this._addCommandFinishedListener();
|
||||
this._addCommandStartedListener();
|
||||
this._addCommandClearedListener();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
@ -141,6 +143,7 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
|
|||
if (c === TerminalCapability.CommandDetection) {
|
||||
this._commandStartedListener?.dispose();
|
||||
this._commandFinishedListener?.dispose();
|
||||
this._commandClearedListener?.dispose();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
@ -171,12 +174,29 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
|
|||
for (const command of capability.commands) {
|
||||
this.registerCommandDecoration(command);
|
||||
}
|
||||
this._commandFinishedListener = capability.onCommandFinished(command => {
|
||||
if (command.command.trim().toLowerCase() === 'clear' || command.command.trim().toLowerCase() === 'cls') {
|
||||
this.clearDecorations();
|
||||
return;
|
||||
this._commandFinishedListener = capability.onCommandFinished(command => this.registerCommandDecoration(command));
|
||||
}
|
||||
|
||||
private _addCommandClearedListener(): void {
|
||||
if (this._commandClearedListener) {
|
||||
return;
|
||||
}
|
||||
const capability = this._capabilities.get(TerminalCapability.CommandDetection);
|
||||
if (!capability) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._commandClearedListener = capability.onCommandInvalidated(commands => {
|
||||
for (const command of commands) {
|
||||
const id = command.marker?.id;
|
||||
if (id) {
|
||||
const match = this._decorations.get(id);
|
||||
if (match) {
|
||||
match.decoration.dispose();
|
||||
dispose(match.disposables);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.registerCommandDecoration(command);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -250,10 +250,10 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal {
|
|||
}
|
||||
|
||||
clearDecorations(): void {
|
||||
this._decorationAddon?.clearDecorations();
|
||||
this._decorationAddon?.dispose();
|
||||
this._decorationAddon = undefined;
|
||||
}
|
||||
|
||||
|
||||
forceRefresh() {
|
||||
this._core.viewport?._innerRefresh();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue