Files
azuredatastudio/src/vs/workbench/contrib/terminal/browser/terminalCommandTracker.ts
Anthony Dresser 87765e8673 Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd

* fix issues with merges

* bump node version in azpipe

* replace license headers

* remove duplicate launch task

* fix build errors

* fix build errors

* fix tslint issues

* working through package and linux build issues

* more work

* wip

* fix packaged builds

* working through linux build errors

* wip

* wip

* wip

* fix mac and linux file limits

* iterate linux pipeline

* disable editor typing

* revert series to parallel

* remove optimize vscode from linux

* fix linting issues

* revert testing change

* add work round for new node

* readd packaging for extensions

* fix issue with angular not resolving decorator dependencies
2019-03-19 17:44:35 -07:00

280 lines
7.7 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Terminal, IMarker } from 'vscode-xterm';
import { ITerminalCommandTracker } from 'vs/workbench/contrib/terminal/common/terminal';
import { IDisposable } from 'vs/base/common/lifecycle';
/**
* The minimum size of the prompt in which to assume the line is a command.
*/
const MINIMUM_PROMPT_LENGTH = 2;
enum Boundary {
Top,
Bottom
}
export const enum ScrollPosition {
Top,
Middle
}
export class TerminalCommandTracker implements ITerminalCommandTracker, IDisposable {
private _currentMarker: IMarker | Boundary = Boundary.Bottom;
private _selectionStart: IMarker | Boundary | null = null;
private _isDisposable: boolean = false;
constructor(
private _xterm: Terminal
) {
this._xterm.on('key', key => this._onKey(key));
}
public dispose(): void {
}
private _onKey(key: string): void {
if (key === '\x0d') {
this._onEnter();
}
// Clear the current marker so successive focus/selection actions are performed from the
// bottom of the buffer
this._currentMarker = Boundary.Bottom;
this._selectionStart = null;
}
private _onEnter(): void {
if (this._xterm._core.buffer.x >= MINIMUM_PROMPT_LENGTH) {
this._xterm.addMarker(0);
}
}
public scrollToPreviousCommand(scrollPosition: ScrollPosition = ScrollPosition.Top, retainSelection: boolean = false): void {
if (!retainSelection) {
this._selectionStart = null;
}
let markerIndex;
if (this._currentMarker === Boundary.Bottom) {
markerIndex = this._xterm.markers.length - 1;
} else if (this._currentMarker === Boundary.Top) {
markerIndex = -1;
} else if (this._isDisposable) {
markerIndex = this._findPreviousCommand();
this._currentMarker.dispose();
this._isDisposable = false;
} else {
markerIndex = this._xterm.markers.indexOf(this._currentMarker) - 1;
}
if (markerIndex < 0) {
this._currentMarker = Boundary.Top;
this._xterm.scrollToTop();
return;
}
this._currentMarker = this._xterm.markers[markerIndex];
this._scrollToMarker(this._currentMarker, scrollPosition);
}
public scrollToNextCommand(scrollPosition: ScrollPosition = ScrollPosition.Top, retainSelection: boolean = false): void {
if (!retainSelection) {
this._selectionStart = null;
}
let markerIndex;
if (this._currentMarker === Boundary.Bottom) {
markerIndex = this._xterm.markers.length;
} else if (this._currentMarker === Boundary.Top) {
markerIndex = 0;
} else if (this._isDisposable) {
markerIndex = this._findNextCommand();
this._currentMarker.dispose();
this._isDisposable = false;
} else {
markerIndex = this._xterm.markers.indexOf(this._currentMarker) + 1;
}
if (markerIndex >= this._xterm.markers.length) {
this._currentMarker = Boundary.Bottom;
this._xterm.scrollToBottom();
return;
}
this._currentMarker = this._xterm.markers[markerIndex];
this._scrollToMarker(this._currentMarker, scrollPosition);
}
private _scrollToMarker(marker: IMarker, position: ScrollPosition): void {
let line = marker.line;
if (position === ScrollPosition.Middle) {
line = Math.max(line - this._xterm.rows / 2, 0);
}
this._xterm.scrollToLine(line);
}
public selectToPreviousCommand(): void {
if (this._selectionStart === null) {
this._selectionStart = this._currentMarker;
}
this.scrollToPreviousCommand(ScrollPosition.Middle, true);
this._selectLines(this._currentMarker, this._selectionStart);
}
public selectToNextCommand(): void {
if (this._selectionStart === null) {
this._selectionStart = this._currentMarker;
}
this.scrollToNextCommand(ScrollPosition.Middle, true);
this._selectLines(this._currentMarker, this._selectionStart);
}
public selectToPreviousLine(): void {
if (this._selectionStart === null) {
this._selectionStart = this._currentMarker;
}
this.scrollToPreviousLine(ScrollPosition.Middle, true);
this._selectLines(this._currentMarker, this._selectionStart);
}
public selectToNextLine(): void {
if (this._selectionStart === null) {
this._selectionStart = this._currentMarker;
}
this.scrollToNextLine(ScrollPosition.Middle, true);
this._selectLines(this._currentMarker, this._selectionStart);
}
private _selectLines(start: IMarker | Boundary, end: IMarker | Boundary | null): void {
if (end === null) {
end = Boundary.Bottom;
}
let startLine = this._getLine(start);
let endLine = this._getLine(end);
if (startLine > endLine) {
const temp = startLine;
startLine = endLine;
endLine = temp;
}
// Subtract a line as the marker is on the line the command run, we do not want the next
// command in the selection for the current command
endLine -= 1;
this._xterm.selectLines(startLine, endLine);
}
private _getLine(marker: IMarker | Boundary): number {
// Use the _second last_ row as the last row is likely the prompt
if (marker === Boundary.Bottom) {
return this._xterm._core.buffer.ybase + this._xterm.rows - 1;
}
if (marker === Boundary.Top) {
return 0;
}
return marker.line;
}
public scrollToPreviousLine(scrollPosition: ScrollPosition = ScrollPosition.Top, retainSelection: boolean = false): void {
if (!retainSelection) {
this._selectionStart = null;
}
if (this._currentMarker === Boundary.Top) {
this._xterm.scrollToTop();
return;
}
if (this._currentMarker === Boundary.Bottom) {
this._currentMarker = this._xterm.addMarker(this._getOffset() - 1);
} else {
const offset = this._getOffset();
if (this._isDisposable) {
this._currentMarker.dispose();
}
this._currentMarker = this._xterm.addMarker(offset - 1);
}
this._isDisposable = true;
this._scrollToMarker(this._currentMarker, scrollPosition);
}
public scrollToNextLine(scrollPosition: ScrollPosition = ScrollPosition.Top, retainSelection: boolean = false): void {
if (!retainSelection) {
this._selectionStart = null;
}
if (this._currentMarker === Boundary.Bottom) {
this._xterm.scrollToBottom();
return;
}
if (this._currentMarker === Boundary.Top) {
this._currentMarker = this._xterm.addMarker(this._getOffset() + 1);
} else {
const offset = this._getOffset();
if (this._isDisposable) {
this._currentMarker.dispose();
}
this._currentMarker = this._xterm.addMarker(offset + 1);
}
this._isDisposable = true;
this._scrollToMarker(this._currentMarker, scrollPosition);
}
private _getOffset(): number {
if (this._currentMarker === Boundary.Bottom) {
return 0;
} else if (this._currentMarker === Boundary.Top) {
return 0 - (this._xterm._core.buffer.ybase + this._xterm._core.buffer.y);
} else {
let offset = this._getLine(this._currentMarker);
offset -= this._xterm._core.buffer.ybase + this._xterm._core.buffer.y;
return offset;
}
}
private _findPreviousCommand(): number {
if (this._currentMarker === Boundary.Top) {
return 0;
} else if (this._currentMarker === Boundary.Bottom) {
return this._xterm.markers.length - 1;
}
let i;
for (i = this._xterm.markers.length - 1; i >= 0; i--) {
if (this._xterm.markers[i].line < this._currentMarker.line) {
return i;
}
}
return -1;
}
private _findNextCommand(): number {
if (this._currentMarker === Boundary.Top) {
return 0;
} else if (this._currentMarker === Boundary.Bottom) {
return this._xterm.markers.length - 1;
}
let i;
for (i = 0; i < this._xterm.markers.length; i++) {
if (this._xterm.markers[i].line > this._currentMarker.line) {
return i;
}
}
return this._xterm.markers.length;
}
}