Move SelectionService into browser (+strict)

This commit is contained in:
Daniel Imms
2019-06-29 18:41:48 -07:00
parent 48f68a7702
commit 3806f67fc0
7 changed files with 85 additions and 72 deletions
+9 -1
View File
@@ -548,10 +548,12 @@ describe('Terminal', () => {
});
describe('with macOptionIsMeta', () => {
let originalIsMac: boolean;
beforeEach(() => {
term.browser.isMac = true;
originalIsMac = term.browser.isMac;
term.options.macOptionIsMeta = true;
});
afterEach(() => term.browser.isMac = originalIsMac);
it('should interfere with the alt key on keyDown', () => {
evKeyDown.altKey = true;
@@ -564,9 +566,12 @@ describe('Terminal', () => {
});
describe('On Mac OS', () => {
let originalIsMac: boolean;
beforeEach(() => {
originalIsMac = term.browser.isMac;
term.browser.isMac = true;
});
afterEach(() => term.browser.isMac = originalIsMac);
it('should not interfere with the alt key on keyDown', () => {
evKeyDown.altKey = true;
@@ -627,9 +632,12 @@ describe('Terminal', () => {
});
describe('On MS Windows', () => {
let originalIsWindows: boolean;
beforeEach(() => {
originalIsWindows = term.browser.isMSWindows;
term.browser.isMSWindows = true;
});
afterEach(() => term.browser.isMSWindows = originalIsWindows);
it('should not interfere with the alt + ctrl key on keyDown', () => {
evKeyPress.altKey = true;
+1 -1
View File
@@ -30,7 +30,7 @@ import { C0 } from 'common/data/EscapeSequences';
import { InputHandler } from './InputHandler';
import { Renderer } from './renderer/Renderer';
import { Linkifier } from './Linkifier';
import { SelectionService } from './SelectionService';
import { SelectionService } from './browser/services/SelectionService';
import * as Browser from 'common/Platform';
import { addDisposableDomListener } from 'browser/Lifecycle';
import * as Strings from './browser/LocalizableStrings';
+2 -2
View File
@@ -4,7 +4,7 @@
*/
export interface ISelectionRedrawRequestEvent {
start: [number, number];
end: [number, number];
start: [number, number] | undefined;
end: [number, number] | undefined;
columnSelectMode: boolean;
}
@@ -13,13 +13,14 @@ import { IBufferService, IOptionsService } from 'common/services/Services';
import { MockCharSizeService, MockMouseService } from 'browser/TestUtils.test';
import { CellData } from 'common/buffer/CellData';
import { IBuffer } from 'common/buffer/Types';
import { isMSWindows } from '../../../out/common/Platform';
class TestSelectionService extends SelectionService {
constructor(
bufferService: IBufferService,
optionsService: IOptionsService
) {
super(() => {}, null, null, new MockCharSizeService(10, 10), bufferService, new MockCoreService(), new MockMouseService(), optionsService);
super(() => {}, null!, null!, new MockCharSizeService(10, 10), bufferService, new MockCoreService(), new MockMouseService(), optionsService);
}
public get model(): SelectionModel { return this._model; }
@@ -97,21 +98,21 @@ describe('SelectionService', () => {
it('should expand selection for wide characters', () => {
// Wide characters use a special format
const data: [number, string, number, number][] = [
[null, '中', 2, '中'.charCodeAt(0)],
[null, '', 0, null],
[null, '文', 2, '文'.charCodeAt(0)],
[null, '', 0, null],
[null, ' ', 1, ' '.charCodeAt(0)],
[null, 'a', 1, 'a'.charCodeAt(0)],
[null, '中', 2, '中'.charCodeAt(0)],
[null, '', 0, null],
[null, '文', 2, '文'.charCodeAt(0)],
[null, '', 0, ''.charCodeAt(0)],
[null, 'b', 1, 'b'.charCodeAt(0)],
[null, ' ', 1, ' '.charCodeAt(0)],
[null, 'f', 1, 'f'.charCodeAt(0)],
[null, 'o', 1, 'o'.charCodeAt(0)],
[null, 'o', 1, 'o'.charCodeAt(0)]
[0, '中', 2, '中'.charCodeAt(0)],
[0, '', 0, 0],
[0, '文', 2, '文'.charCodeAt(0)],
[0, '', 0, 0],
[0, ' ', 1, ' '.charCodeAt(0)],
[0, 'a', 1, 'a'.charCodeAt(0)],
[0, '中', 2, '中'.charCodeAt(0)],
[0, '', 0, 0],
[0, '文', 2, '文'.charCodeAt(0)],
[0, '', 0, ''.charCodeAt(0)],
[0, 'b', 1, 'b'.charCodeAt(0)],
[0, ' ', 1, ' '.charCodeAt(0)],
[0, 'f', 1, 'f'.charCodeAt(0)],
[0, 'o', 1, 'o'.charCodeAt(0)],
[0, 'o', 1, 'o'.charCodeAt(0)]
];
const line = new BufferLine(data.length);
for (let i = 0; i < data.length; ++i) line.setCell(i, CellData.fromCharData(data[i]));
@@ -188,7 +189,7 @@ describe('SelectionService', () => {
it('should expand upwards or downards for wrapped lines', () => {
buffer.lines.set(0, stringToRow(' foo'));
buffer.lines.set(1, stringToRow('bar '));
buffer.lines.get(1).isWrapped = true;
buffer.lines.get(1)!.isWrapped = true;
selectionService.selectWordAt([1, 1]);
assert.equal(selectionService.selectionText, 'foobar');
selectionService.model.clearSelection();
@@ -202,10 +203,10 @@ describe('SelectionService', () => {
buffer.lines.set(2, stringToRow('bbbbbbbbbbbbbbbbbbbb'));
buffer.lines.set(3, stringToRow('cccccccccccccccccccc'));
buffer.lines.set(4, stringToRow('bar '));
buffer.lines.get(1).isWrapped = true;
buffer.lines.get(2).isWrapped = true;
buffer.lines.get(3).isWrapped = true;
buffer.lines.get(4).isWrapped = true;
buffer.lines.get(1)!.isWrapped = true;
buffer.lines.get(2)!.isWrapped = true;
buffer.lines.get(3)!.isWrapped = true;
buffer.lines.get(4)!.isWrapped = true;
selectionService.selectWordAt([18, 0]);
assert.equal(selectionService.selectionText, expectedText);
selectionService.model.clearSelection();
@@ -359,6 +360,8 @@ describe('SelectionService', () => {
buffer.lines.set(3, stringToRow('4'));
buffer.lines.set(4, stringToRow('5'));
selectionService.selectAll();
console.log(selectionService.selectionText.length);
console.log(isMSWindows);
assert.equal(selectionService.selectionText, '1\n2\n3\n4\n5');
});
});
@@ -73,7 +73,7 @@ export class SelectionService implements ISelectionService {
* The amount to scroll every drag scroll update (depends on how far the mouse
* drag is above or below the terminal).
*/
private _dragScrollAmount: number;
private _dragScrollAmount: number = 0;
/**
* The current selection mode.
@@ -84,12 +84,12 @@ export class SelectionService implements ISelectionService {
* A setInterval timer that is active while the mouse is down whose callback
* scrolls the viewport when necessary.
*/
private _dragScrollIntervalTimer: NodeJS.Timer;
private _dragScrollIntervalTimer: number | undefined;
/**
* The animation frame ID used for refreshing the selection.
*/
private _refreshAnimationFrame: number;
private _refreshAnimationFrame: number | undefined;
/**
* Whether selection is enabled.
@@ -101,7 +101,7 @@ export class SelectionService implements ISelectionService {
private _trimListener: IDisposable;
private _workCell: CellData = new CellData();
private _mouseDownTimeStamp: number;
private _mouseDownTimeStamp: number = 0;
private _onLinuxMouseSelection = new EventEmitter<string>();
public get onLinuxMouseSelection(): IEvent<string> { return this._onLinuxMouseSelection.event; }
@@ -120,7 +120,17 @@ export class SelectionService implements ISelectionService {
private readonly _mouseService: IMouseService,
private readonly _optionsService: IOptionsService
) {
this._initListeners();
// Init listeners
this._mouseMoveListener = event => this._onMouseMove(<MouseEvent>event);
this._mouseUpListener = event => this._onMouseUp(<MouseEvent>event);
this._coreService.onUserInput(() => {
if (this.hasSelection) {
this.clearSelection();
}
});
this._trimListener = this._bufferService.buffer.lines.onTrim(amount => this._onTrim(amount));
this._bufferService.buffers.onBufferActivate(e => this._onBufferActivate(e));
this.enable();
this._model = new SelectionModel(this._bufferService);
@@ -131,29 +141,10 @@ export class SelectionService implements ISelectionService {
this._removeMouseDownListeners();
}
/**
* Initializes listener variables.
*/
private _initListeners(): void {
this._mouseMoveListener = event => this._onMouseMove(<MouseEvent>event);
this._mouseUpListener = event => this._onMouseUp(<MouseEvent>event);
this._coreService.onUserInput(() => {
if (this.hasSelection) {
this.clearSelection();
}
});
this.initBuffersListeners();
}
public reset(): void {
this.clearSelection();
}
public initBuffersListeners(): void {
this._trimListener = this._bufferService.buffer.lines.onTrim(amount => this._onTrim(amount));
this._bufferService.buffers.onBufferActivate(e => this._onBufferActivate(e));
}
/**
* Disables the selection manager. This is useful for when terminal mouse
* are enabled.
@@ -170,8 +161,8 @@ export class SelectionService implements ISelectionService {
this._enabled = true;
}
public get selectionStart(): [number, number] { return this._model.finalSelectionStart; }
public get selectionEnd(): [number, number] { return this._model.finalSelectionEnd; }
public get selectionStart(): [number, number] | undefined { return this._model.finalSelectionStart; }
public get selectionEnd(): [number, number] | undefined { return this._model.finalSelectionEnd; }
/**
* Gets whether there is an active text selection.
@@ -217,7 +208,7 @@ export class SelectionService implements ISelectionService {
for (let i = start[1] + 1; i <= end[1] - 1; i++) {
const bufferLine = buffer.lines.get(i);
const lineText = buffer.translateBufferLineToString(i, true);
if (bufferLine.isWrapped) {
if (bufferLine!.isWrapped) {
result[result.length - 1] += lineText;
} else {
result.push(lineText);
@@ -228,7 +219,7 @@ export class SelectionService implements ISelectionService {
if (start[1] !== end[1]) {
const bufferLine = buffer.lines.get(end[1]);
const lineText = buffer.translateBufferLineToString(end[1], true, 0, end[0]);
if (bufferLine.isWrapped) {
if (bufferLine!.isWrapped) {
result[result.length - 1] += lineText;
} else {
result.push(lineText);
@@ -281,7 +272,7 @@ export class SelectionService implements ISelectionService {
* selection state.
*/
private _refresh(): void {
this._refreshAnimationFrame = null;
this._refreshAnimationFrame = undefined;
this._onRedrawRequest.fire({
start: this._model.finalSelectionStart,
end: this._model.finalSelectionEnd,
@@ -298,7 +289,7 @@ export class SelectionService implements ISelectionService {
const start = this._model.finalSelectionStart;
const end = this._model.finalSelectionEnd;
if (!start || !end) {
if (!start || !end || !coords) {
return false;
}
@@ -320,7 +311,7 @@ export class SelectionService implements ISelectionService {
const coords = this._getMouseBufferCoords(event);
if (coords) {
this._selectWordAt(coords, false);
this._model.selectionEnd = null;
this._model.selectionEnd = undefined;
this.refresh(true);
}
}
@@ -359,10 +350,10 @@ export class SelectionService implements ISelectionService {
* Gets the 0-based [x, y] buffer coordinates of the current mouse event.
* @param event The mouse event.
*/
private _getMouseBufferCoords(event: MouseEvent): [number, number] {
private _getMouseBufferCoords(event: MouseEvent): [number, number] | undefined {
const coords = this._mouseService.getCoords(event, this._screenElement, this._bufferService.cols, this._bufferService.rows, true);
if (!coords) {
return null;
return undefined;
}
// Convert to 0-based
@@ -461,9 +452,11 @@ export class SelectionService implements ISelectionService {
*/
private _addMouseDownListeners(): void {
// Listen on the document so that dragging outside of viewport works
this._screenElement.ownerDocument.addEventListener('mousemove', this._mouseMoveListener);
this._screenElement.ownerDocument.addEventListener('mouseup', this._mouseUpListener);
this._dragScrollIntervalTimer = setInterval(() => this._dragScroll(), DRAG_SCROLL_INTERVAL);
if (this._screenElement.ownerDocument) {
this._screenElement.ownerDocument.addEventListener('mousemove', this._mouseMoveListener);
this._screenElement.ownerDocument.addEventListener('mouseup', this._mouseUpListener);
}
this._dragScrollIntervalTimer = window.setInterval(() => this._dragScroll(), DRAG_SCROLL_INTERVAL);
}
/**
@@ -475,7 +468,7 @@ export class SelectionService implements ISelectionService {
this._screenElement.ownerDocument.removeEventListener('mouseup', this._mouseUpListener);
}
clearInterval(this._dragScrollIntervalTimer);
this._dragScrollIntervalTimer = null;
this._dragScrollIntervalTimer = undefined;
}
/**
@@ -504,7 +497,7 @@ export class SelectionService implements ISelectionService {
if (!this._model.selectionStart) {
return;
}
this._model.selectionEnd = null;
this._model.selectionEnd = undefined;
// Ensure the line exists
const line = this._bufferService.buffer.lines.get(this._model.selectionStart[1]);
@@ -568,6 +561,11 @@ export class SelectionService implements ISelectionService {
// to be sent to the pty.
event.stopImmediatePropagation();
// Something went wrong
if (!this._model.selectionStart) {
throw new Error('Selection start position was not set before mousemove event');
}
// Record the previous position so we know whether to redraw the selection
// at the end.
const previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null;
@@ -609,7 +607,8 @@ export class SelectionService implements ISelectionService {
// have a character.
const buffer = this._bufferService.buffer;
if (this._model.selectionEnd[1] < buffer.lines.length) {
if (buffer.lines.get(this._model.selectionEnd[1]).hasWidth(this._model.selectionEnd[0]) === 0) {
const line = buffer.lines.get(this._model.selectionEnd[1]);
if (line && line.hasWidth(this._model.selectionEnd[0]) === 0) {
this._model.selectionEnd[0]++;
}
}
@@ -627,6 +626,9 @@ export class SelectionService implements ISelectionService {
* scrolling of the viewport.
*/
private _dragScroll(): void {
if (!this._model.selectionEnd || !this._model.selectionStart) {
return;
}
if (this._dragScrollAmount) {
this._scrollLines(this._dragScrollAmount, false);
// Re-evaluate selection
@@ -724,16 +726,16 @@ export class SelectionService implements ISelectionService {
* Gets positional information for the word at the coordinated specified.
* @param coords The coordinates to get the word at.
*/
private _getWordAt(coords: [number, number], allowWhitespaceOnlySelection: boolean, followWrappedLinesAbove: boolean = true, followWrappedLinesBelow: boolean = true): IWordPosition {
private _getWordAt(coords: [number, number], allowWhitespaceOnlySelection: boolean, followWrappedLinesAbove: boolean = true, followWrappedLinesBelow: boolean = true): IWordPosition | undefined {
// Ensure coords are within viewport (eg. not within scroll bar)
if (coords[0] >= this._bufferService.cols) {
return null;
return undefined;
}
const buffer = this._bufferService.buffer;
const bufferLine = buffer.lines.get(coords[1]);
if (!bufferLine) {
return null;
return undefined;
}
const line = buffer.translateBufferLineToString(coords[1], false);
@@ -840,7 +842,7 @@ export class SelectionService implements ISelectionService {
- rightLongCharOffset); // The number of additional chars right of the initial char (inclusive) added by columns with strings longer than 1 (emojis)
if (!allowWhitespaceOnlySelection && line.slice(startIndex, endIndex).trim() === '') {
return null;
return undefined;
}
// Recurse upwards if the line is wrapped and the word wraps to the above line
+3 -3
View File
@@ -51,11 +51,11 @@ export interface IRenderService {
export interface ISelectionService {
readonly selectionText: string;
readonly hasSelection: boolean;
readonly selectionStart: [number, number];
readonly selectionEnd: [number, number];
readonly selectionStart: [number, number] | undefined;
readonly selectionEnd: [number, number] | undefined;
readonly onLinuxMouseSelection: IEvent<string>;
readonly onRedrawRequest: IEvent<ISelectionRedrawRequestEvent>
readonly onRedrawRequest: IEvent<ISelectionRedrawRequestEvent>;
readonly onSelectionChange: IEvent<void>;
disable(): void;
+1 -1
View File
@@ -104,6 +104,7 @@ export interface ITerminalOptions {
tabStopWidth: number;
theme: ITheme;
windowsMode: boolean;
wordSeparator: string;
[key: string]: any;
cancelEvents: boolean;
@@ -112,7 +113,6 @@ export interface ITerminalOptions {
screenKeys: boolean;
termName: string;
useFlowControl: boolean;
wordSeparator?: string;
}
export interface ITheme {