Merge branch 'master' into benchmark_integration

This commit is contained in:
Jörg Breitbart
2019-06-09 12:37:06 +02:00
95 changed files with 2432 additions and 2815 deletions
@@ -17,7 +17,7 @@ const height = 600;
describe('AttachAddon', () => {
before(async function(): Promise<any> {
this.timeout(10000);
this.timeout(20000);
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
@@ -32,7 +32,7 @@ describe('AttachAddon', () => {
});
beforeEach(async function(): Promise<any> {
this.timeout(5000);
this.timeout(20000);
await page.goto(APP);
});
+3 -3
View File
@@ -39,7 +39,7 @@ export class FitAddon implements ITerminalAddon {
// Force a full render
if (this._terminal.rows !== dims.rows || this._terminal.cols !== dims.cols) {
core._renderCoordinator.clear();
core._renderService.clear();
this._terminal.resize(dims.cols, dims.rows);
}
}
@@ -71,8 +71,8 @@ export class FitAddon implements ITerminalAddon {
const availableHeight = parentElementHeight - elementPaddingVer;
const availableWidth = parentElementWidth - elementPaddingHor - core.viewport.scrollBarWidth;
const geometry = {
cols: Math.floor(availableWidth / core._renderCoordinator.dimensions.actualCellWidth),
rows: Math.floor(availableHeight / core._renderCoordinator.dimensions.actualCellHeight)
cols: Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth),
rows: Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight)
};
return geometry;
}
+2 -2
View File
@@ -307,8 +307,8 @@ function addDomListener(element: HTMLElement, type: string, handler: (...args: a
function updateTerminalSize(): void {
const cols = parseInt((<HTMLInputElement>document.getElementById(`opt-cols`)).value, 10);
const rows = parseInt((<HTMLInputElement>document.getElementById(`opt-rows`)).value, 10);
const width = (cols * term._core._renderCoordinator.dimensions.actualCellWidth + term._core.viewport.scrollBarWidth).toString() + 'px';
const height = (rows * term._core._renderCoordinator.dimensions.actualCellHeight).toString() + 'px';
const width = (cols * term._core._renderService.dimensions.actualCellWidth + term._core.viewport.scrollBarWidth).toString() + 'px';
const height = (rows * term._core._renderService.dimensions.actualCellHeight).toString() + 'px';
terminalContainer.style.width = width;
terminalContainer.style.height = height;
fitAddon.fit();
+1 -2
View File
@@ -51,8 +51,7 @@ const clientConfig = {
extensions: [ '.tsx', '.ts', '.js' ],
alias: {
common: path.resolve('./out/common'),
core: path.resolve('./out/core'),
ui: path.resolve('./out/ui')
browser: path.resolve('./out/browser')
}
},
output: {
+9 -8
View File
@@ -4,13 +4,14 @@
*/
import * as Strings from './Strings';
import { ITerminal, IBuffer } from './Types';
import { ITerminal } from './Types';
import { IBuffer } from 'common/buffer/Types';
import { isMac } from 'common/Platform';
import { RenderDebouncer } from 'ui/RenderDebouncer';
import { addDisposableDomListener } from 'ui/Lifecycle';
import { RenderDebouncer } from 'browser/RenderDebouncer';
import { addDisposableDomListener } from 'browser/Lifecycle';
import { Disposable } from 'common/Lifecycle';
import { ScreenDprMonitor } from 'ui/ScreenDprMonitor';
import { IRenderDimensions } from './renderer/Types';
import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
import { IRenderDimensions } from 'browser/renderer/Types';
const MAX_ROWS_TO_READ = 20;
@@ -84,11 +85,11 @@ export class AccessibilityManager extends Disposable {
this.register(this._terminal.onRender(e => this._refreshRows(e.start, e.end)));
this.register(this._terminal.onScroll(() => this._refreshRows()));
// Line feed is an issue as the prompt won't be read out after a command is run
this.register(this._terminal.addDisposableListener('a11y.char', (char) => this._onChar(char)));
this.register(this._terminal.onA11yChar(char => this._onChar(char)));
this.register(this._terminal.onLineFeed(() => this._onChar('\n')));
this.register(this._terminal.addDisposableListener('a11y.tab', spaceCount => this._onTab(spaceCount)));
this.register(this._terminal.onA11yTab(spaceCount => this._onTab(spaceCount)));
this.register(this._terminal.onKey(e => this._onKey(e.key)));
this.register(this._terminal.addDisposableListener('blur', () => this._clearLiveRegion()));
this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
this._screenDprMonitor = new ScreenDprMonitor();
this.register(this._screenDprMonitor);
-1403
View File
File diff suppressed because it is too large Load Diff
-54
View File
@@ -1,54 +0,0 @@
/**
* Copyright (c) 2016 The xterm.js authors. All rights reserved.
* @license MIT
*/
import jsdom = require('jsdom');
import { ICharMeasure } from './Types';
import { assert } from 'chai';
import { CharMeasure } from './CharMeasure';
describe('CharMeasure', () => {
let dom: jsdom.JSDOM;
let window: Window;
let document: Document;
let container: HTMLElement;
let charMeasure: ICharMeasure;
beforeEach(() => {
dom = new jsdom.JSDOM('');
window = dom.window;
document = window.document;
container = document.createElement('div');
document.body.appendChild(container);
charMeasure = new CharMeasure(document, container);
});
describe('measure', () => {
it('should have _measureElement', () => {
assert.isDefined((<any>charMeasure)._measureElement, 'new CharMeasure() should have created _measureElement');
});
it('should be performed sync', () => {
// Mock getBoundingClientRect since jsdom doesn't have a layout engine
(<any>charMeasure)._measureElement.getBoundingClientRect = () => {
return { width: 1, height: 1 };
};
charMeasure.measure({});
assert.equal(charMeasure.height, 1);
assert.equal(charMeasure.width, 1);
});
it('should NOT do a measure when the parent is hidden', done => {
charMeasure.measure({});
setTimeout(() => {
const firstWidth = charMeasure.width;
container.style.display = 'none';
container.style.fontSize = '2em';
charMeasure.measure({});
assert.equal(charMeasure.width, firstWidth);
done();
}, 0);
});
});
});
-58
View File
@@ -1,58 +0,0 @@
/**
* Copyright (c) 2016 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ICharMeasure, ITerminalOptions } from './Types';
import { EventEmitter2, IEvent } from 'common/EventEmitter2';
/**
* Utility class that measures the size of a character. Measurements are done in
* the DOM rather than with a canvas context because support for extracting the
* height of characters is patchy across browsers.
*/
export class CharMeasure implements ICharMeasure {
private _document: Document;
private _parentElement: HTMLElement;
private _measureElement: HTMLElement;
private _width: number;
private _height: number;
private _onCharSizeChanged = new EventEmitter2<void>();
public get onCharSizeChanged(): IEvent<void> { return this._onCharSizeChanged.event; }
constructor(document: Document, parentElement: HTMLElement) {
this._document = document;
this._parentElement = parentElement;
this._measureElement = this._document.createElement('span');
this._measureElement.classList.add('xterm-char-measure-element');
this._measureElement.textContent = 'W';
this._measureElement.setAttribute('aria-hidden', 'true');
this._parentElement.appendChild(this._measureElement);
}
public get width(): number {
return this._width;
}
public get height(): number {
return this._height;
}
public measure(options: ITerminalOptions): void {
this._measureElement.style.fontFamily = options.fontFamily;
this._measureElement.style.fontSize = `${options.fontSize}px`;
const geometry = this._measureElement.getBoundingClientRect();
// The element is likely currently display:none, we should retain the
// previous value.
if (geometry.width === 0 || geometry.height === 0) {
return;
}
const adjustedHeight = Math.ceil(geometry.height);
if (this._width !== geometry.width || this._height !== adjustedHeight) {
this._width = geometry.width;
this._height = adjustedHeight;
this._onCharSizeChanged.fire();
}
}
}
-1
View File
@@ -63,7 +63,6 @@ export function pasteHandler(ev: ClipboardEvent, term: ITerminal): void {
text = bracketTextForPaste(text, term.bracketedPasteMode);
term.handler(text);
term.textarea.value = '';
term.emit('paste', text);
term.cancel(ev);
};
+2 -5
View File
@@ -6,6 +6,7 @@
import { assert } from 'chai';
import { CompositionHelper } from './CompositionHelper';
import { ITerminal } from './Types';
import { MockCharSizeService } from 'browser/TestUtils.test';
describe('CompositionHelper', () => {
let terminal: ITerminal;
@@ -48,16 +49,12 @@ describe('CompositionHelper', () => {
buffer: {
isCursorInViewport: true
},
charMeasure: {
height: 10,
width: 10
},
options: {
lineHeight: 1
}
} as any;
handledText = '';
compositionHelper = new CompositionHelper(textarea, compositionView, terminal);
compositionHelper = new CompositionHelper(textarea, compositionView, terminal, new MockCharSizeService(10, 10));
});
describe('Input', () => {
+5 -3
View File
@@ -4,6 +4,7 @@
*/
import { ITerminal } from './Types';
import { ICharSizeService } from 'browser/services/Services';
interface IPosition {
start: number;
@@ -42,7 +43,8 @@ export class CompositionHelper {
constructor(
private _textarea: HTMLTextAreaElement,
private _compositionView: HTMLElement,
private _terminal: ITerminal
private _terminal: ITerminal,
private _charSizeService: ICharSizeService
) {
this._isComposing = false;
this._isSendingComposition = false;
@@ -195,9 +197,9 @@ export class CompositionHelper {
}
if (this._terminal.buffer.isCursorInViewport) {
const cellHeight = Math.ceil(this._terminal.charMeasure.height * this._terminal.options.lineHeight);
const cellHeight = Math.ceil(this._charSizeService.height * this._terminal.options.lineHeight);
const cursorTop = this._terminal.buffer.y * cellHeight;
const cursorLeft = this._terminal.buffer.x * this._terminal.charMeasure.width;
const cursorLeft = this._terminal.buffer.x * this._charSizeService.width;
this._compositionView.style.left = cursorLeft + 'px';
this._compositionView.style.top = cursorTop + 'px';
+2 -2
View File
@@ -7,8 +7,8 @@ import { assert, expect } from 'chai';
import { InputHandler } from './InputHandler';
import { MockInputHandlingTerminal, TestTerminal } from './TestUtils.test';
import { Terminal } from './Terminal';
import { IBufferLine } from 'core/Types';
import { CellData, Attributes, AttributeData, DEFAULT_ATTR_DATA } from 'core/buffer/BufferLine';
import { IBufferLine } from 'common/Types';
import { CellData, Attributes, AttributeData, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
describe('InputHandler', () => {
describe('save and restore cursor', () => {
+14 -14
View File
@@ -6,16 +6,16 @@
import { IInputHandler, IInputHandlingTerminal } from './Types';
import { C0, C1 } from 'common/data/EscapeSequences';
import { CHARSETS, DEFAULT_CHARSET } from 'core/data/Charsets';
import { wcwidth } from './common/CharWidth';
import { EscapeSequenceParser } from 'core/parser/EscapeSequenceParser';
import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets';
import { wcwidth } from 'common/CharWidth';
import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser';
import { IDisposable } from 'xterm';
import { Disposable } from 'common/Lifecycle';
import { concat } from 'common/TypedArrayUtils';
import { StringToUtf32, stringFromCodePoint, utf32ToString, Utf8ToUtf32 } from 'core/input/TextDecoder';
import { CellData, Attributes, FgFlags, BgFlags, AttributeData, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR_DATA } from 'core/buffer/BufferLine';
import { EventEmitter2, IEvent } from 'common/EventEmitter2';
import { IParsingState, IDcsHandler, IEscapeSequenceParser } from 'core/parser/Types';
import { StringToUtf32, stringFromCodePoint, utf32ToString, Utf8ToUtf32 } from 'common/input/TextDecoder';
import { CellData, Attributes, FgFlags, BgFlags, AttributeData, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { IParsingState, IDcsHandler, IEscapeSequenceParser } from 'common/parser/Types';
/**
* Map collect to glevel. Used in `selectCharset`.
@@ -108,13 +108,13 @@ export class InputHandler extends Disposable implements IInputHandler {
private _utf8Decoder: Utf8ToUtf32 = new Utf8ToUtf32();
private _workCell: CellData = new CellData();
private _onCursorMove = new EventEmitter2<void>();
private _onCursorMove = new EventEmitter<void>();
public get onCursorMove(): IEvent<void> { return this._onCursorMove.event; }
private _onData = new EventEmitter2<string>();
private _onData = new EventEmitter<string>();
public get onData(): IEvent<string> { return this._onData.event; }
private _onLineFeed = new EventEmitter2<void>();
private _onLineFeed = new EventEmitter<void>();
public get onLineFeed(): IEvent<void> { return this._onLineFeed.event; }
private _onScroll = new EventEmitter2<number>();
private _onScroll = new EventEmitter<number>();
public get onScroll(): IEvent<number> { return this._onScroll.event; }
constructor(
@@ -342,7 +342,7 @@ export class InputHandler extends Disposable implements IInputHandler {
buffer = this._terminal.buffer;
if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
this._terminal.emit('cursormove');
this._onCursorMove.fire();
}
}
@@ -377,7 +377,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
if (screenReaderMode) {
this._terminal.emit('a11y.char', stringFromCodePoint(code));
this._terminal.onA11yCharEmitter.fire(stringFromCodePoint(code));
}
// insert combining char at last cursor position
@@ -542,7 +542,7 @@ export class InputHandler extends Disposable implements IInputHandler {
const originalX = this._terminal.buffer.x;
this._terminal.buffer.x = this._terminal.buffer.nextStop();
if (this._terminal.options.screenReaderMode) {
this._terminal.emit('a11y.tab', this._terminal.buffer.x - originalX);
this._terminal.onA11yTabEmitter.fire(this._terminal.buffer.x - originalX);
}
}
+2 -2
View File
@@ -5,11 +5,11 @@
import { assert } from 'chai';
import { IMouseZoneManager, IMouseZone, ILinkMatcher, ITerminal } from './Types';
import { IBufferLine } from 'core/Types';
import { IBufferLine } from 'common/Types';
import { Linkifier } from './Linkifier';
import { MockBuffer, MockTerminal, TestTerminal } from './TestUtils.test';
import { CircularList } from 'common/CircularList';
import { BufferLine, CellData } from 'core/buffer/BufferLine';
import { BufferLine, CellData } from 'common/buffer/BufferLine';
class TestLinkifier extends Linkifier {
constructor(terminal: ITerminal) {
+6 -5
View File
@@ -3,10 +3,11 @@
* @license MIT
*/
import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, ITerminal, IBufferStringIteratorResult, IMouseZoneManager } from './Types';
import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, ITerminal, IMouseZoneManager } from './Types';
import { IBufferStringIteratorResult } from 'common/buffer/Types';
import { MouseZone } from './MouseZoneManager';
import { getStringCellWidth } from 'common/CharWidth';
import { EventEmitter2, IEvent } from 'common/EventEmitter2';
import { EventEmitter, IEvent } from 'common/EventEmitter';
/**
* The Linkifier applies links to rows shortly after they have been refreshed.
@@ -33,11 +34,11 @@ export class Linkifier implements ILinkifier {
private _nextLinkMatcherId = 0;
private _rowsToLinkify: { start: number, end: number };
private _onLinkHover = new EventEmitter2<ILinkifierEvent>();
private _onLinkHover = new EventEmitter<ILinkifierEvent>();
public get onLinkHover(): IEvent<ILinkifierEvent> { return this._onLinkHover.event; }
private _onLinkLeave = new EventEmitter2<ILinkifierEvent>();
private _onLinkLeave = new EventEmitter<ILinkifierEvent>();
public get onLinkLeave(): IEvent<ILinkifierEvent> { return this._onLinkLeave.event; }
private _onLinkTooltip = new EventEmitter2<ILinkifierEvent>();
private _onLinkTooltip = new EventEmitter<ILinkifierEvent>();
public get onLinkTooltip(): IEvent<ILinkifierEvent> { return this._onLinkTooltip.event; }
constructor(
-64
View File
@@ -1,64 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import jsdom = require('jsdom');
import { assert } from 'chai';
import { MouseHelper } from './MouseHelper';
import { MockCharMeasure, MockRenderer } from './TestUtils.test';
const CHAR_WIDTH = 10;
const CHAR_HEIGHT = 20;
describe('MouseHelper.getCoords', () => {
let dom: jsdom.JSDOM;
let window: Window;
let document: Document;
let mouseHelper: MouseHelper;
let charMeasure: MockCharMeasure;
beforeEach(() => {
dom = new jsdom.JSDOM('');
window = dom.window;
document = window.document;
charMeasure = new MockCharMeasure();
charMeasure.width = CHAR_WIDTH;
charMeasure.height = CHAR_HEIGHT;
const renderer = new MockRenderer();
renderer.dimensions = <any>{
actualCellWidth: CHAR_WIDTH,
actualCellHeight: CHAR_HEIGHT
};
mouseHelper = new MouseHelper(renderer as any);
});
describe('when charMeasure is not initialized', () => {
it('should return null', () => {
charMeasure = new MockCharMeasure();
assert.equal(mouseHelper.getCoords({ clientX: 0, clientY: 0 }, document.createElement('div'), charMeasure, 10, 10), null);
});
});
it('should return the cell that was clicked', () => {
let coords: [number, number];
coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH / 2, clientY: CHAR_HEIGHT / 2 }, document.createElement('div'), charMeasure, 10, 10);
assert.deepEqual(coords, [1, 1]);
coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH, clientY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 10, 10);
assert.deepEqual(coords, [1, 1]);
coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH, clientY: CHAR_HEIGHT + 1 }, document.createElement('div'), charMeasure, 10, 10);
assert.deepEqual(coords, [1, 2]);
coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH + 1, clientY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 10, 10);
assert.deepEqual(coords, [2, 1]);
});
it('should ensure the coordinates are returned within the terminal bounds', () => {
let coords: [number, number];
coords = mouseHelper.getCoords({ clientX: -1, clientY: -1 }, document.createElement('div'), charMeasure, 10, 10);
assert.deepEqual(coords, [1, 1]);
// Event are double the cols/rows
coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH * 20, clientY: CHAR_HEIGHT * 20 }, document.createElement('div'), charMeasure, 10, 10);
assert.deepEqual(coords, [10, 10], 'coordinates should never come back as larger than the terminal');
});
});
+2 -2
View File
@@ -5,7 +5,7 @@
import { ITerminal, IMouseZoneManager, IMouseZone } from './Types';
import { Disposable } from 'common/Lifecycle';
import { addDisposableDomListener } from 'ui/Lifecycle';
import { addDisposableDomListener } from 'browser/Lifecycle';
const HOVER_DURATION = 500;
@@ -203,7 +203,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager {
}
private _findZoneEventAt(e: MouseEvent): IMouseZone {
const coords = this._terminal.mouseHelper.getCoords(e, this._terminal.screenElement, this._terminal.charMeasure, this._terminal.cols, this._terminal.rows);
const coords = this._terminal.mouseHelper.getCoords(e, this._terminal.screenElement, this._terminal.cols, this._terminal.rows);
if (!coords) {
return null;
}
+39 -32
View File
@@ -4,14 +4,17 @@
*/
import { assert } from 'chai';
import { CharMeasure } from './CharMeasure';
import { SelectionManager, SelectionMode } from './SelectionManager';
import { SelectionModel } from './SelectionModel';
import { BufferSet } from './BufferSet';
import { ITerminal, IBuffer } from './Types';
import { IBufferLine } from 'core/Types';
import { BufferSet } from 'common/buffer/BufferSet';
import { ITerminal } from './Types';
import { IBuffer } from 'common/buffer/Types';
import { IBufferLine } from 'common/Types';
import { MockTerminal } from './TestUtils.test';
import { BufferLine, CellData } from 'core/buffer/BufferLine';
import { MockOptionsService, MockBufferService } from 'common/TestUtils.test';
import { BufferLine, CellData } from 'common/buffer/BufferLine';
import { IBufferService } from 'common/services/Services';
import { MockCharSizeService } from 'browser/TestUtils.test';
class TestMockTerminal extends MockTerminal {
emit(event: string, data: any): void {}
@@ -20,9 +23,9 @@ class TestMockTerminal extends MockTerminal {
class TestSelectionManager extends SelectionManager {
constructor(
terminal: ITerminal,
charMeasure: CharMeasure
bufferService: IBufferService
) {
super(terminal, charMeasure);
super(terminal, new MockCharSizeService(10, 10), bufferService);
}
public get model(): SelectionModel { return this._model; }
@@ -42,17 +45,21 @@ class TestSelectionManager extends SelectionManager {
describe('SelectionManager', () => {
let terminal: ITerminal;
let buffer: IBuffer;
let bufferService: IBufferService;
let selectionManager: TestSelectionManager;
beforeEach(() => {
terminal = new TestMockTerminal();
(terminal as any).cols = 80;
(terminal as any).rows = 2;
terminal.options.scrollback = 100;
terminal.buffers = new BufferSet(terminal);
bufferService = new MockBufferService(20, 20);
terminal.buffers = new BufferSet(
new MockOptionsService({ scrollback: 100 }),
bufferService
);
terminal.cols = 20;
terminal.rows = 20;
terminal.buffer = terminal.buffers.active;
buffer = terminal.buffer;
selectionManager = new TestSelectionManager(terminal, null);
selectionManager = new TestSelectionManager(terminal, bufferService);
});
function stringToRow(text: string): IBufferLine {
@@ -192,36 +199,36 @@ describe('SelectionManager', () => {
assert.equal(selectionManager.selectionText, 'ij"');
});
it('should expand upwards or downards for wrapped lines', () => {
buffer.lines.set(0, stringToRow(' foo'));
buffer.lines.set(1, stringToRow('bar '));
buffer.lines.set(0, stringToRow(' foo'));
buffer.lines.set(1, stringToRow('bar '));
buffer.lines.get(1).isWrapped = true;
selectionManager.selectWordAt([1, 1]);
assert.equal(selectionManager.selectionText, 'foobar');
selectionManager.model.clearSelection();
selectionManager.selectWordAt([78, 0]);
selectionManager.selectWordAt([18, 0]);
assert.equal(selectionManager.selectionText, 'foobar');
});
it('should expand both upwards and downwards for word wrapped over many lines', () => {
const expectedText = 'fooaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbar';
buffer.lines.set(0, stringToRow(' foo'));
buffer.lines.set(1, stringToRow('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'));
buffer.lines.set(2, stringToRow('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'));
buffer.lines.set(3, stringToRow('cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'));
buffer.lines.set(4, stringToRow('bar '));
const expectedText = 'fooaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbccccccccccccccccccccbar';
buffer.lines.set(0, stringToRow(' foo'));
buffer.lines.set(1, stringToRow('aaaaaaaaaaaaaaaaaaaa'));
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;
selectionManager.selectWordAt([78, 0]);
selectionManager.selectWordAt([18, 0]);
assert.equal(selectionManager.selectionText, expectedText);
selectionManager.model.clearSelection();
selectionManager.selectWordAt([40, 1]);
selectionManager.selectWordAt([10, 1]);
assert.equal(selectionManager.selectionText, expectedText);
selectionManager.model.clearSelection();
selectionManager.selectWordAt([40, 2]);
selectionManager.selectWordAt([10, 2]);
assert.equal(selectionManager.selectionText, expectedText);
selectionManager.model.clearSelection();
selectionManager.selectWordAt([40, 3]);
selectionManager.selectWordAt([10, 3]);
assert.equal(selectionManager.selectionText, expectedText);
selectionManager.model.clearSelection();
selectionManager.selectWordAt([1, 4]);
@@ -342,7 +349,7 @@ describe('SelectionManager', () => {
selectionManager.selectLineAt(0);
assert.equal(selectionManager.selectionText, 'foo bar', 'The selected text is correct');
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 0], 'The actual selection spans the entire column');
assert.deepEqual(selectionManager.model.finalSelectionEnd, [bufferService.cols, 0], 'The actual selection spans the entire column');
});
it('should select the entire wrapped line', () => {
buffer.lines.set(0, stringToRow('foo'));
@@ -352,7 +359,7 @@ describe('SelectionManager', () => {
selectionManager.selectLineAt(0);
assert.equal(selectionManager.selectionText, 'foobar', 'The selected text is correct');
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 1], 'The actual selection spans the entire column');
assert.deepEqual(selectionManager.model.finalSelectionEnd, [bufferService.cols, 1], 'The actual selection spans the entire column');
});
});
@@ -365,7 +372,7 @@ describe('SelectionManager', () => {
buffer.lines.set(3, stringToRow('4'));
buffer.lines.set(4, stringToRow('5'));
selectionManager.selectAll();
terminal.buffer.ybase = buffer.lines.length - terminal.rows;
terminal.buffer.ybase = buffer.lines.length - bufferService.rows;
assert.equal(selectionManager.selectionText, '1\n2\n3\n4\n5');
});
});
@@ -378,7 +385,7 @@ describe('SelectionManager', () => {
buffer.lines.set(2, stringToRow('3'));
selectionManager.selectLines(1, 1);
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 1]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 1]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [bufferService.cols, 1]);
});
it('should select multiple lines', () => {
buffer.lines.length = 5;
@@ -389,7 +396,7 @@ describe('SelectionManager', () => {
buffer.lines.set(4, stringToRow('5'));
selectionManager.selectLines(1, 3);
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 1]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 3]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [bufferService.cols, 3]);
});
it('should select the to the start when requesting a negative row', () => {
buffer.lines.length = 2;
@@ -397,7 +404,7 @@ describe('SelectionManager', () => {
buffer.lines.set(1, stringToRow('2'));
selectionManager.selectLines(-1, 0);
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 0]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [bufferService.cols, 0]);
});
it('should select the to the end when requesting beyond the final row', () => {
buffer.lines.length = 2;
@@ -405,7 +412,7 @@ describe('SelectionManager', () => {
buffer.lines.set(1, stringToRow('2'));
selectionManager.selectLines(1, 2);
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 1]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 1]);
assert.deepEqual(selectionManager.model.finalSelectionEnd, [bufferService.cols, 1]);
});
});
+16 -13
View File
@@ -3,16 +3,18 @@
* @license MIT
*/
import { ITerminal, ISelectionManager, IBuffer, ISelectionRedrawRequestEvent } from './Types';
import { IBufferLine } from 'core/Types';
import { MouseHelper } from './MouseHelper';
import { ITerminal, ISelectionManager, ISelectionRedrawRequestEvent } from './Types';
import { IBuffer } from 'common/buffer/Types';
import { IBufferLine } from 'common/Types';
import { MouseHelper } from 'browser/input/MouseHelper';
import * as Browser from 'common/Platform';
import { CharMeasure } from './CharMeasure';
import { SelectionModel } from './SelectionModel';
import { AltClickHandler } from './handlers/AltClickHandler';
import { CellData } from 'core/buffer/BufferLine';
import { CellData } from 'common/buffer/BufferLine';
import { IDisposable } from 'xterm';
import { EventEmitter2, IEvent } from 'common/EventEmitter2';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { ICharSizeService } from 'browser/services/Services';
import { IBufferService } from 'common/services/Services';
/**
* The number of pixels the mouse needs to be above or below the viewport in
@@ -108,21 +110,22 @@ export class SelectionManager implements ISelectionManager {
private _mouseDownTimeStamp: number;
private _onLinuxMouseSelection = new EventEmitter2<string>();
private _onLinuxMouseSelection = new EventEmitter<string>();
public get onLinuxMouseSelection(): IEvent<string> { return this._onLinuxMouseSelection.event; }
private _onRedrawRequest = new EventEmitter2<ISelectionRedrawRequestEvent>();
private _onRedrawRequest = new EventEmitter<ISelectionRedrawRequestEvent>();
public get onRedrawRequest(): IEvent<ISelectionRedrawRequestEvent> { return this._onRedrawRequest.event; }
private _onSelectionChange = new EventEmitter2<void>();
private _onSelectionChange = new EventEmitter<void>();
public get onSelectionChange(): IEvent<void> { return this._onSelectionChange.event; }
constructor(
private _terminal: ITerminal,
private _charMeasure: CharMeasure
private _charSizeService: ICharSizeService,
bufferService: IBufferService
) {
this._initListeners();
this.enable();
this._model = new SelectionModel(_terminal);
this._model = new SelectionModel(_terminal, bufferService);
this._activeSelectionMode = SelectionMode.NORMAL;
}
@@ -354,7 +357,7 @@ export class SelectionManager implements ISelectionManager {
* @param event The mouse event.
*/
private _getMouseBufferCoords(event: MouseEvent): [number, number] {
const coords = this._terminal.mouseHelper.getCoords(event, this._terminal.screenElement, this._charMeasure, this._terminal.cols, this._terminal.rows, true);
const coords = this._terminal.mouseHelper.getCoords(event, this._terminal.screenElement, this._terminal.cols, this._terminal.rows, true);
if (!coords) {
return null;
}
@@ -375,7 +378,7 @@ export class SelectionManager implements ISelectionManager {
*/
private _getMouseEventScrollAmount(event: MouseEvent): number {
let offset = MouseHelper.getCoordsRelativeToElement(event, this._terminal.screenElement)[1];
const terminalHeight = this._terminal.rows * Math.ceil(this._charMeasure.height * this._terminal.options.lineHeight);
const terminalHeight = this._terminal.rows * Math.ceil(this._charSizeService.height * this._terminal.options.lineHeight);
if (offset >= 0 && offset <= terminalHeight) {
return 0;
}
+12 -8
View File
@@ -6,14 +6,17 @@
import { assert } from 'chai';
import { ITerminal } from './Types';
import { SelectionModel } from './SelectionModel';
import { BufferSet } from './BufferSet';
import { BufferSet } from 'common/buffer/BufferSet';
import { MockTerminal } from './TestUtils.test';
import { MockOptionsService, MockBufferService } from 'common/TestUtils.test';
import { IBufferService } from 'common/services/Services';
class TestSelectionModel extends SelectionModel {
constructor(
terminal: ITerminal
terminal: ITerminal,
bufferService: IBufferService
) {
super(terminal);
super(terminal, bufferService);
}
}
@@ -23,13 +26,14 @@ describe('SelectionManager', () => {
beforeEach(() => {
terminal = new MockTerminal();
(terminal as any).cols = 80;
(terminal as any).rows = 2;
terminal.options.scrollback = 10;
terminal.buffers = new BufferSet(terminal);
const bufferService = new MockBufferService(80, 2);
terminal.buffers = new BufferSet(
new MockOptionsService({ scrollback: 10 }),
bufferService
);
terminal.buffer = terminal.buffers.active;
model = new TestSelectionModel(terminal);
model = new TestSelectionModel(terminal, bufferService);
});
describe('clearSelection', () => {

Some files were not shown because too many files have changed in this diff Show More