Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Terminal line selection #47991

Merged
merged 11 commits into from
May 19, 2018
2 changes: 2 additions & 0 deletions src/vs/workbench/parts/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,8 @@ export interface ITerminalCommandTracker {
scrollToNextCommand(): void;
selectToPreviousCommand(): void;
selectToNextCommand(): void;
selectToPreviousLine(): void;
selectToNextLine(): void;
}

export interface ITerminalProcessManager extends IDisposable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { getTerminalDefaultShellUnixLike, getTerminalDefaultShellWindows } from
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KillTerminalAction, ClearSelectionTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, ShowNextFindTermTerminalFindWidgetAction, ShowPreviousFindTermTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitTerminalAction, SplitInActiveWorkspaceTerminalAction, FocusPreviousPaneTerminalAction, FocusNextPaneTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, ResizePaneDownTerminalAction, ScrollToPreviousCommandAction, ScrollToNextCommandAction, SelectToPreviousCommandAction, SelectToNextCommandAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions';
import { KillTerminalAction, ClearSelectionTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, ShowNextFindTermTerminalFindWidgetAction, ShowPreviousFindTermTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitTerminalAction, SplitInActiveWorkspaceTerminalAction, FocusPreviousPaneTerminalAction, FocusNextPaneTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, ResizePaneDownTerminalAction, ScrollToPreviousCommandAction, ScrollToNextCommandAction, SelectToPreviousCommandAction, SelectToNextCommandAction, SelectToPreviousLineAction, SelectToNextLineAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions';
import { Registry } from 'vs/platform/registry/common/platform';
import { ShowAllCommandsAction } from 'vs/workbench/parts/quickopen/browser/commandsHandler';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
Expand Down Expand Up @@ -296,7 +296,9 @@ configurationRegistry.registerConfiguration({
ScrollToPreviousCommandAction.ID,
ScrollToNextCommandAction.ID,
SelectToPreviousCommandAction.ID,
SelectToNextCommandAction.ID
SelectToNextCommandAction.ID,
SelectToPreviousLineAction.ID,
SelectToNextLineAction.ID
].sort()
},
'terminal.integrated.env.osx': {
Expand Down Expand Up @@ -512,6 +514,8 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextComm
primary: null,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow }
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select To Next Command', category);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToPreviousLineAction, SelectToPreviousLineAction.ID, SelectToPreviousLineAction.LABEL), 'Terminal: Select To Previous Line', category);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextLineAction, SelectToNextLineAction.ID, SelectToNextLineAction.LABEL), 'Terminal: Select To Next Line', category);

terminalCommands.setup();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1130,3 +1130,45 @@ export class SelectToNextCommandAction extends Action {
return TPromise.as(void 0);
}
}

export class SelectToPreviousLineAction extends Action {
public static readonly ID = 'workbench.action.terminal.selectToPreviousLine';
public static readonly LABEL = nls.localize('workbench.action.terminal.selectToPreviousLine', "Select To Previous Line");

constructor(
id: string, label: string,
@ITerminalService private terminalService: ITerminalService
) {
super(id, label);
}

public run(): TPromise<any> {
const instance = this.terminalService.getActiveInstance();
if (instance) {
instance.commandTracker.selectToPreviousLine();
instance.focus();
}
return TPromise.as(void 0);
}
}

export class SelectToNextLineAction extends Action {
public static readonly ID = 'workbench.action.terminal.selectToNextLine';
public static readonly LABEL = nls.localize('workbench.action.terminal.selectToNextLine', "Select To Next Line");

constructor(
id: string, label: string,
@ITerminalService private terminalService: ITerminalService
) {
super(id, label);
}

public run(): TPromise<any> {
const instance = this.terminalService.getActiveInstance();
if (instance) {
instance.commandTracker.selectToNextLine();
instance.focus();
}
return TPromise.as(void 0);
}
}
119 changes: 119 additions & 0 deletions src/vs/workbench/parts/terminal/node/terminalCommandTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export enum ScrollPosition {
export class TerminalCommandTracker implements ITerminalCommandTracker {
private _currentMarker: IMarker | Boundary = Boundary.Bottom;
private _selectionStart: IMarker | Boundary | null = null;
private _isDisposable: boolean = false;

constructor(
private _xterm: Terminal
Expand Down Expand Up @@ -58,6 +59,10 @@ export class TerminalCommandTracker implements ITerminalCommandTracker {
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;
}
Expand All @@ -82,6 +87,10 @@ export class TerminalCommandTracker implements ITerminalCommandTracker {
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;
}
Expand Down Expand Up @@ -120,6 +129,24 @@ export class TerminalCommandTracker implements ITerminalCommandTracker {
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;
Expand Down Expand Up @@ -153,4 +180,96 @@ export class TerminalCommandTracker implements ITerminalCommandTracker {

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 {
let 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 {
let 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.buffer.ybase + this._xterm.buffer.y);
} else {
let offset = this._getLine(this._currentMarker);
offset -= this._xterm.buffer.ybase + this._xterm.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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,39 @@ suite('Workbench - TerminalCommandTracker', () => {
// commandTracker.selectToNextCommand();
// assert.equal(xterm.getSelection(), '\n');
// });
// test('should select to the next and previous lines & commands', () => {
// (<any>window).matchMedia = () => {
// return { addListener: () => {} };
// };
// xterm.open(document.createElement('div'));

// syncWrite(xterm, '\r0');
// syncWrite(xterm, '\n\r1');
// syncWrite(xterm, '\x1b[3G'); // Move cursor to column 3
// xterm.emit('key', '\x0d'); // Mark line
// assert.equal(xterm.markers[0].line, 10);
// syncWrite(xterm, '\n\r2');
// syncWrite(xterm, '\x1b[3G'); // Move cursor to column 3
// xterm.emit('key', '\x0d'); // Mark line
// assert.equal(xterm.markers[1].line, 11);
// syncWrite(xterm, '\n\r3');

// assert.equal(xterm.buffer.ybase, 3);
// assert.equal(xterm.buffer.ydisp, 3);

// assert.equal(xterm.getSelection(), '');
// commandTracker.selectToPreviousLine();
// assert.equal(xterm.getSelection(), '2');
// commandTracker.selectToNextLine();
// commandTracker.selectToNextLine();
// assert.equal(xterm.getSelection(), '3');
// commandTracker.selectToPreviousCommand();
// commandTracker.selectToPreviousCommand();
// commandTracker.selectToNextLine();
// assert.equal(xterm.getSelection(), '2');
// commandTracker.selectToPreviousCommand();
// commandTracker.selectToPreviousLine();
// assert.equal(xterm.getSelection(), '0\r\n1\r\n2');
// });
});
});