Merge pull request #2208 from Tyriar/buffer_common
Move buffer to common
This commit is contained in:
@@ -4,7 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as Strings from './Strings';
|
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 { isMac } from 'common/Platform';
|
||||||
import { RenderDebouncer } from 'browser/RenderDebouncer';
|
import { RenderDebouncer } from 'browser/RenderDebouncer';
|
||||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||||
|
|||||||
-1403
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
import { IInputHandler, IInputHandlingTerminal } from './Types';
|
import { IInputHandler, IInputHandlingTerminal } from './Types';
|
||||||
import { C0, C1 } from 'common/data/EscapeSequences';
|
import { C0, C1 } from 'common/data/EscapeSequences';
|
||||||
import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets';
|
import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets';
|
||||||
import { wcwidth } from './common/CharWidth';
|
import { wcwidth } from 'common/CharWidth';
|
||||||
import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser';
|
import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser';
|
||||||
import { IDisposable } from 'xterm';
|
import { IDisposable } from 'xterm';
|
||||||
import { Disposable } from 'common/Lifecycle';
|
import { Disposable } from 'common/Lifecycle';
|
||||||
|
|||||||
+2
-1
@@ -3,7 +3,8 @@
|
|||||||
* @license MIT
|
* @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 { MouseZone } from './MouseZoneManager';
|
||||||
import { getStringCellWidth } from 'common/CharWidth';
|
import { getStringCellWidth } from 'common/CharWidth';
|
||||||
import { EventEmitter2, IEvent } from 'common/EventEmitter2';
|
import { EventEmitter2, IEvent } from 'common/EventEmitter2';
|
||||||
|
|||||||
@@ -6,11 +6,14 @@
|
|||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { SelectionManager, SelectionMode } from './SelectionManager';
|
import { SelectionManager, SelectionMode } from './SelectionManager';
|
||||||
import { SelectionModel } from './SelectionModel';
|
import { SelectionModel } from './SelectionModel';
|
||||||
import { BufferSet } from './BufferSet';
|
import { BufferSet } from 'common/buffer/BufferSet';
|
||||||
import { ITerminal, IBuffer } from './Types';
|
import { ITerminal } from './Types';
|
||||||
|
import { IBuffer } from 'common/buffer/Types';
|
||||||
import { IBufferLine } from 'common/Types';
|
import { IBufferLine } from 'common/Types';
|
||||||
import { MockTerminal, MockCharSizeService } from './TestUtils.test';
|
import { MockTerminal, MockCharSizeService } from './TestUtils.test';
|
||||||
|
import { MockOptionsService, MockBufferService } from 'common/TestUtils.test';
|
||||||
import { BufferLine, CellData } from 'common/buffer/BufferLine';
|
import { BufferLine, CellData } from 'common/buffer/BufferLine';
|
||||||
|
import { IBufferService } from 'common/services/Services';
|
||||||
|
|
||||||
class TestMockTerminal extends MockTerminal {
|
class TestMockTerminal extends MockTerminal {
|
||||||
emit(event: string, data: any): void {}
|
emit(event: string, data: any): void {}
|
||||||
@@ -18,9 +21,10 @@ class TestMockTerminal extends MockTerminal {
|
|||||||
|
|
||||||
class TestSelectionManager extends SelectionManager {
|
class TestSelectionManager extends SelectionManager {
|
||||||
constructor(
|
constructor(
|
||||||
terminal: ITerminal
|
terminal: ITerminal,
|
||||||
|
bufferService: IBufferService
|
||||||
) {
|
) {
|
||||||
super(terminal, new MockCharSizeService(10, 10));
|
super(terminal, new MockCharSizeService(10, 10), bufferService);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get model(): SelectionModel { return this._model; }
|
public get model(): SelectionModel { return this._model; }
|
||||||
@@ -40,17 +44,21 @@ class TestSelectionManager extends SelectionManager {
|
|||||||
describe('SelectionManager', () => {
|
describe('SelectionManager', () => {
|
||||||
let terminal: ITerminal;
|
let terminal: ITerminal;
|
||||||
let buffer: IBuffer;
|
let buffer: IBuffer;
|
||||||
|
let bufferService: IBufferService;
|
||||||
let selectionManager: TestSelectionManager;
|
let selectionManager: TestSelectionManager;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
terminal = new TestMockTerminal();
|
terminal = new TestMockTerminal();
|
||||||
(terminal as any).cols = 80;
|
bufferService = new MockBufferService(20, 20);
|
||||||
(terminal as any).rows = 2;
|
terminal.buffers = new BufferSet(
|
||||||
terminal.options.scrollback = 100;
|
new MockOptionsService({ scrollback: 100 }),
|
||||||
terminal.buffers = new BufferSet(terminal);
|
bufferService
|
||||||
|
);
|
||||||
|
terminal.cols = 20;
|
||||||
|
terminal.rows = 20;
|
||||||
terminal.buffer = terminal.buffers.active;
|
terminal.buffer = terminal.buffers.active;
|
||||||
buffer = terminal.buffer;
|
buffer = terminal.buffer;
|
||||||
selectionManager = new TestSelectionManager(terminal);
|
selectionManager = new TestSelectionManager(terminal, bufferService);
|
||||||
});
|
});
|
||||||
|
|
||||||
function stringToRow(text: string): IBufferLine {
|
function stringToRow(text: string): IBufferLine {
|
||||||
@@ -190,36 +198,36 @@ describe('SelectionManager', () => {
|
|||||||
assert.equal(selectionManager.selectionText, 'ij"');
|
assert.equal(selectionManager.selectionText, 'ij"');
|
||||||
});
|
});
|
||||||
it('should expand upwards or downards for wrapped lines', () => {
|
it('should expand upwards or downards for wrapped lines', () => {
|
||||||
buffer.lines.set(0, stringToRow(' foo'));
|
buffer.lines.set(0, stringToRow(' foo'));
|
||||||
buffer.lines.set(1, stringToRow('bar '));
|
buffer.lines.set(1, stringToRow('bar '));
|
||||||
buffer.lines.get(1).isWrapped = true;
|
buffer.lines.get(1).isWrapped = true;
|
||||||
selectionManager.selectWordAt([1, 1]);
|
selectionManager.selectWordAt([1, 1]);
|
||||||
assert.equal(selectionManager.selectionText, 'foobar');
|
assert.equal(selectionManager.selectionText, 'foobar');
|
||||||
selectionManager.model.clearSelection();
|
selectionManager.model.clearSelection();
|
||||||
selectionManager.selectWordAt([78, 0]);
|
selectionManager.selectWordAt([18, 0]);
|
||||||
assert.equal(selectionManager.selectionText, 'foobar');
|
assert.equal(selectionManager.selectionText, 'foobar');
|
||||||
});
|
});
|
||||||
it('should expand both upwards and downwards for word wrapped over many lines', () => {
|
it('should expand both upwards and downwards for word wrapped over many lines', () => {
|
||||||
const expectedText = 'fooaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbar';
|
const expectedText = 'fooaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbccccccccccccccccccccbar';
|
||||||
buffer.lines.set(0, stringToRow(' foo'));
|
buffer.lines.set(0, stringToRow(' foo'));
|
||||||
buffer.lines.set(1, stringToRow('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'));
|
buffer.lines.set(1, stringToRow('aaaaaaaaaaaaaaaaaaaa'));
|
||||||
buffer.lines.set(2, stringToRow('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'));
|
buffer.lines.set(2, stringToRow('bbbbbbbbbbbbbbbbbbbb'));
|
||||||
buffer.lines.set(3, stringToRow('cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'));
|
buffer.lines.set(3, stringToRow('cccccccccccccccccccc'));
|
||||||
buffer.lines.set(4, stringToRow('bar '));
|
buffer.lines.set(4, stringToRow('bar '));
|
||||||
buffer.lines.get(1).isWrapped = true;
|
buffer.lines.get(1).isWrapped = true;
|
||||||
buffer.lines.get(2).isWrapped = true;
|
buffer.lines.get(2).isWrapped = true;
|
||||||
buffer.lines.get(3).isWrapped = true;
|
buffer.lines.get(3).isWrapped = true;
|
||||||
buffer.lines.get(4).isWrapped = true;
|
buffer.lines.get(4).isWrapped = true;
|
||||||
selectionManager.selectWordAt([78, 0]);
|
selectionManager.selectWordAt([18, 0]);
|
||||||
assert.equal(selectionManager.selectionText, expectedText);
|
assert.equal(selectionManager.selectionText, expectedText);
|
||||||
selectionManager.model.clearSelection();
|
selectionManager.model.clearSelection();
|
||||||
selectionManager.selectWordAt([40, 1]);
|
selectionManager.selectWordAt([10, 1]);
|
||||||
assert.equal(selectionManager.selectionText, expectedText);
|
assert.equal(selectionManager.selectionText, expectedText);
|
||||||
selectionManager.model.clearSelection();
|
selectionManager.model.clearSelection();
|
||||||
selectionManager.selectWordAt([40, 2]);
|
selectionManager.selectWordAt([10, 2]);
|
||||||
assert.equal(selectionManager.selectionText, expectedText);
|
assert.equal(selectionManager.selectionText, expectedText);
|
||||||
selectionManager.model.clearSelection();
|
selectionManager.model.clearSelection();
|
||||||
selectionManager.selectWordAt([40, 3]);
|
selectionManager.selectWordAt([10, 3]);
|
||||||
assert.equal(selectionManager.selectionText, expectedText);
|
assert.equal(selectionManager.selectionText, expectedText);
|
||||||
selectionManager.model.clearSelection();
|
selectionManager.model.clearSelection();
|
||||||
selectionManager.selectWordAt([1, 4]);
|
selectionManager.selectWordAt([1, 4]);
|
||||||
@@ -340,7 +348,7 @@ describe('SelectionManager', () => {
|
|||||||
selectionManager.selectLineAt(0);
|
selectionManager.selectLineAt(0);
|
||||||
assert.equal(selectionManager.selectionText, 'foo bar', 'The selected text is correct');
|
assert.equal(selectionManager.selectionText, 'foo bar', 'The selected text is correct');
|
||||||
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]);
|
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', () => {
|
it('should select the entire wrapped line', () => {
|
||||||
buffer.lines.set(0, stringToRow('foo'));
|
buffer.lines.set(0, stringToRow('foo'));
|
||||||
@@ -350,7 +358,7 @@ describe('SelectionManager', () => {
|
|||||||
selectionManager.selectLineAt(0);
|
selectionManager.selectLineAt(0);
|
||||||
assert.equal(selectionManager.selectionText, 'foobar', 'The selected text is correct');
|
assert.equal(selectionManager.selectionText, 'foobar', 'The selected text is correct');
|
||||||
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]);
|
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');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -363,7 +371,7 @@ describe('SelectionManager', () => {
|
|||||||
buffer.lines.set(3, stringToRow('4'));
|
buffer.lines.set(3, stringToRow('4'));
|
||||||
buffer.lines.set(4, stringToRow('5'));
|
buffer.lines.set(4, stringToRow('5'));
|
||||||
selectionManager.selectAll();
|
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');
|
assert.equal(selectionManager.selectionText, '1\n2\n3\n4\n5');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -376,7 +384,7 @@ describe('SelectionManager', () => {
|
|||||||
buffer.lines.set(2, stringToRow('3'));
|
buffer.lines.set(2, stringToRow('3'));
|
||||||
selectionManager.selectLines(1, 1);
|
selectionManager.selectLines(1, 1);
|
||||||
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 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', () => {
|
it('should select multiple lines', () => {
|
||||||
buffer.lines.length = 5;
|
buffer.lines.length = 5;
|
||||||
@@ -387,7 +395,7 @@ describe('SelectionManager', () => {
|
|||||||
buffer.lines.set(4, stringToRow('5'));
|
buffer.lines.set(4, stringToRow('5'));
|
||||||
selectionManager.selectLines(1, 3);
|
selectionManager.selectLines(1, 3);
|
||||||
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 1]);
|
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', () => {
|
it('should select the to the start when requesting a negative row', () => {
|
||||||
buffer.lines.length = 2;
|
buffer.lines.length = 2;
|
||||||
@@ -395,7 +403,7 @@ describe('SelectionManager', () => {
|
|||||||
buffer.lines.set(1, stringToRow('2'));
|
buffer.lines.set(1, stringToRow('2'));
|
||||||
selectionManager.selectLines(-1, 0);
|
selectionManager.selectLines(-1, 0);
|
||||||
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 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', () => {
|
it('should select the to the end when requesting beyond the final row', () => {
|
||||||
buffer.lines.length = 2;
|
buffer.lines.length = 2;
|
||||||
@@ -403,7 +411,7 @@ describe('SelectionManager', () => {
|
|||||||
buffer.lines.set(1, stringToRow('2'));
|
buffer.lines.set(1, stringToRow('2'));
|
||||||
selectionManager.selectLines(1, 2);
|
selectionManager.selectLines(1, 2);
|
||||||
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 1]);
|
assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 1]);
|
||||||
assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 1]);
|
assert.deepEqual(selectionManager.model.finalSelectionEnd, [bufferService.cols, 1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ITerminal, ISelectionManager, IBuffer, ISelectionRedrawRequestEvent } from './Types';
|
import { ITerminal, ISelectionManager, ISelectionRedrawRequestEvent } from './Types';
|
||||||
|
import { IBuffer } from 'common/buffer/Types';
|
||||||
import { IBufferLine } from 'common/Types';
|
import { IBufferLine } from 'common/Types';
|
||||||
import { MouseHelper } from './MouseHelper';
|
import { MouseHelper } from './MouseHelper';
|
||||||
import * as Browser from 'common/Platform';
|
import * as Browser from 'common/Platform';
|
||||||
@@ -13,6 +14,7 @@ import { CellData } from 'common/buffer/BufferLine';
|
|||||||
import { IDisposable } from 'xterm';
|
import { IDisposable } from 'xterm';
|
||||||
import { EventEmitter2, IEvent } from 'common/EventEmitter2';
|
import { EventEmitter2, IEvent } from 'common/EventEmitter2';
|
||||||
import { ICharSizeService } from 'browser/services/Services';
|
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
|
* The number of pixels the mouse needs to be above or below the viewport in
|
||||||
@@ -117,12 +119,13 @@ export class SelectionManager implements ISelectionManager {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _terminal: ITerminal,
|
private _terminal: ITerminal,
|
||||||
private _charSizeService: ICharSizeService
|
private _charSizeService: ICharSizeService,
|
||||||
|
bufferService: IBufferService
|
||||||
) {
|
) {
|
||||||
this._initListeners();
|
this._initListeners();
|
||||||
this.enable();
|
this.enable();
|
||||||
|
|
||||||
this._model = new SelectionModel(_terminal);
|
this._model = new SelectionModel(_terminal, bufferService);
|
||||||
this._activeSelectionMode = SelectionMode.NORMAL;
|
this._activeSelectionMode = SelectionMode.NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,17 @@
|
|||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { ITerminal } from './Types';
|
import { ITerminal } from './Types';
|
||||||
import { SelectionModel } from './SelectionModel';
|
import { SelectionModel } from './SelectionModel';
|
||||||
import { BufferSet } from './BufferSet';
|
import { BufferSet } from 'common/buffer/BufferSet';
|
||||||
import { MockTerminal } from './TestUtils.test';
|
import { MockTerminal } from './TestUtils.test';
|
||||||
|
import { MockOptionsService, MockBufferService } from 'common/TestUtils.test';
|
||||||
|
import { IBufferService } from 'common/services/Services';
|
||||||
|
|
||||||
class TestSelectionModel extends SelectionModel {
|
class TestSelectionModel extends SelectionModel {
|
||||||
constructor(
|
constructor(
|
||||||
terminal: ITerminal
|
terminal: ITerminal,
|
||||||
|
bufferService: IBufferService
|
||||||
) {
|
) {
|
||||||
super(terminal);
|
super(terminal, bufferService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,13 +26,14 @@ describe('SelectionManager', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
terminal = new MockTerminal();
|
terminal = new MockTerminal();
|
||||||
(terminal as any).cols = 80;
|
const bufferService = new MockBufferService(80, 2);
|
||||||
(terminal as any).rows = 2;
|
terminal.buffers = new BufferSet(
|
||||||
terminal.options.scrollback = 10;
|
new MockOptionsService({ scrollback: 10 }),
|
||||||
terminal.buffers = new BufferSet(terminal);
|
bufferService
|
||||||
|
);
|
||||||
terminal.buffer = terminal.buffers.active;
|
terminal.buffer = terminal.buffers.active;
|
||||||
|
|
||||||
model = new TestSelectionModel(terminal);
|
model = new TestSelectionModel(terminal, bufferService);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('clearSelection', () => {
|
describe('clearSelection', () => {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ITerminal } from './Types';
|
import { ITerminal } from './Types';
|
||||||
|
import { IBufferService } from 'common/services/Services';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a selection within the buffer. This model only cares about column
|
* Represents a selection within the buffer. This model only cares about column
|
||||||
@@ -33,7 +34,8 @@ export class SelectionModel {
|
|||||||
public selectionEnd: [number, number];
|
public selectionEnd: [number, number];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _terminal: ITerminal
|
private _terminal: ITerminal,
|
||||||
|
private _bufferService: IBufferService
|
||||||
) {
|
) {
|
||||||
this.clearSelection();
|
this.clearSelection();
|
||||||
}
|
}
|
||||||
@@ -69,7 +71,7 @@ export class SelectionModel {
|
|||||||
*/
|
*/
|
||||||
public get finalSelectionEnd(): [number, number] {
|
public get finalSelectionEnd(): [number, number] {
|
||||||
if (this.isSelectAllActive) {
|
if (this.isSelectAllActive) {
|
||||||
return [this._terminal.cols, this._terminal.buffer.ybase + this._terminal.rows - 1];
|
return [this._bufferService.cols, this._terminal.buffer.ybase + this._bufferService.rows - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.selectionStart) {
|
if (!this.selectionStart) {
|
||||||
@@ -79,8 +81,8 @@ export class SelectionModel {
|
|||||||
// Use the selection start + length if the end doesn't exist or they're reversed
|
// Use the selection start + length if the end doesn't exist or they're reversed
|
||||||
if (!this.selectionEnd || this.areSelectionValuesReversed()) {
|
if (!this.selectionEnd || this.areSelectionValuesReversed()) {
|
||||||
const startPlusLength = this.selectionStart[0] + this.selectionStartLength;
|
const startPlusLength = this.selectionStart[0] + this.selectionStartLength;
|
||||||
if (startPlusLength > this._terminal.cols) {
|
if (startPlusLength > this._bufferService.cols) {
|
||||||
return [startPlusLength % this._terminal.cols, this.selectionStart[1] + Math.floor(startPlusLength / this._terminal.cols)];
|
return [startPlusLength % this._bufferService.cols, this.selectionStart[1] + Math.floor(startPlusLength / this._bufferService.cols)];
|
||||||
}
|
}
|
||||||
return [startPlusLength, this.selectionStart[1]];
|
return [startPlusLength, this.selectionStart[1]];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -379,10 +379,10 @@ describe('Terminal', () => {
|
|||||||
describe('scrollLines', () => {
|
describe('scrollLines', () => {
|
||||||
let startYDisp: number;
|
let startYDisp: number;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
for (let i = 0; i < term.rows * 2; i++) {
|
for (let i = 0; i < INIT_ROWS * 2; i++) {
|
||||||
term.writeln('test');
|
term.writeln('test');
|
||||||
}
|
}
|
||||||
startYDisp = term.rows + 1;
|
startYDisp = INIT_ROWS + 1;
|
||||||
});
|
});
|
||||||
it('should scroll a single line', () => {
|
it('should scroll a single line', () => {
|
||||||
assert.equal(term.buffer.ydisp, startYDisp);
|
assert.equal(term.buffer.ydisp, startYDisp);
|
||||||
|
|||||||
+14
-15
@@ -23,8 +23,8 @@
|
|||||||
|
|
||||||
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharacterJoinerHandler, IMouseZoneManager } from './Types';
|
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharacterJoinerHandler, IMouseZoneManager } from './Types';
|
||||||
import { IRenderer } from './renderer/Types';
|
import { IRenderer } from './renderer/Types';
|
||||||
import { BufferSet } from './BufferSet';
|
import { BufferSet } from 'common/buffer/BufferSet';
|
||||||
import { Buffer } from './Buffer';
|
import { Buffer } from 'common/buffer/Buffer';
|
||||||
import { CompositionHelper } from './CompositionHelper';
|
import { CompositionHelper } from './CompositionHelper';
|
||||||
import { EventEmitter } from 'common/EventEmitter';
|
import { EventEmitter } from 'common/EventEmitter';
|
||||||
import { Viewport } from './Viewport';
|
import { Viewport } from './Viewport';
|
||||||
@@ -51,10 +51,11 @@ import { Attributes, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
|||||||
import { applyWindowsMode } from './WindowsMode';
|
import { applyWindowsMode } from './WindowsMode';
|
||||||
import { ColorManager } from 'browser/ColorManager';
|
import { ColorManager } from 'browser/ColorManager';
|
||||||
import { RenderCoordinator } from './renderer/RenderCoordinator';
|
import { RenderCoordinator } from './renderer/RenderCoordinator';
|
||||||
import { IOptionsService } from 'common/services/Services';
|
import { IOptionsService, IBufferService } from 'common/services/Services';
|
||||||
import { OptionsService } from 'common/services/OptionsService';
|
import { OptionsService } from 'common/services/OptionsService';
|
||||||
import { ICharSizeService } from 'browser/services/Services';
|
import { ICharSizeService } from 'browser/services/Services';
|
||||||
import { CharSizeService } from 'browser/services/CharSizeService';
|
import { CharSizeService } from 'browser/services/CharSizeService';
|
||||||
|
import { BufferService, MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService';
|
||||||
|
|
||||||
// Let it work inside Node.js for automated testing purposes.
|
// Let it work inside Node.js for automated testing purposes.
|
||||||
const document = (typeof window !== 'undefined') ? window.document : null;
|
const document = (typeof window !== 'undefined') ? window.document : null;
|
||||||
@@ -75,9 +76,6 @@ const WRITE_BUFFER_PAUSE_THRESHOLD = 5;
|
|||||||
const WRITE_TIMEOUT_MS = 12;
|
const WRITE_TIMEOUT_MS = 12;
|
||||||
const WRITE_BUFFER_LENGTH_THRESHOLD = 50;
|
const WRITE_BUFFER_LENGTH_THRESHOLD = 50;
|
||||||
|
|
||||||
const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars
|
|
||||||
const MINIMUM_ROWS = 1;
|
|
||||||
|
|
||||||
export class Terminal extends EventEmitter implements ITerminal, IDisposable, IInputHandlingTerminal {
|
export class Terminal extends EventEmitter implements ITerminal, IDisposable, IInputHandlingTerminal {
|
||||||
public textarea: HTMLTextAreaElement;
|
public textarea: HTMLTextAreaElement;
|
||||||
public element: HTMLElement;
|
public element: HTMLElement;
|
||||||
@@ -108,6 +106,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
private _customKeyEventHandler: CustomKeyEventHandler;
|
private _customKeyEventHandler: CustomKeyEventHandler;
|
||||||
|
|
||||||
// common services
|
// common services
|
||||||
|
private _bufferService: IBufferService;
|
||||||
public optionsService: IOptionsService;
|
public optionsService: IOptionsService;
|
||||||
|
|
||||||
// browser services
|
// browser services
|
||||||
@@ -188,8 +187,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
// bufferline to clone/copy from for new blank lines
|
// bufferline to clone/copy from for new blank lines
|
||||||
private _blankLine: IBufferLine = null;
|
private _blankLine: IBufferLine = null;
|
||||||
|
|
||||||
public cols: number;
|
public get cols(): number { return this._bufferService.cols; }
|
||||||
public rows: number;
|
public get rows(): number { return this._bufferService.rows; }
|
||||||
|
|
||||||
private _onCursorMove = new EventEmitter2<void>();
|
private _onCursorMove = new EventEmitter2<void>();
|
||||||
public get onCursorMove(): IEvent<void> { return this._onCursorMove.event; }
|
public get onCursorMove(): IEvent<void> { return this._onCursorMove.event; }
|
||||||
@@ -226,7 +225,11 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
options: ITerminalOptions = {}
|
options: ITerminalOptions = {}
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
// Initialize common services
|
||||||
this.optionsService = new OptionsService(options);
|
this.optionsService = new OptionsService(options);
|
||||||
|
this._bufferService = new BufferService(this.optionsService);
|
||||||
|
|
||||||
this._setupOptionsListeners();
|
this._setupOptionsListeners();
|
||||||
|
|
||||||
// this.options = clone(options);
|
// this.options = clone(options);
|
||||||
@@ -263,9 +266,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
private _setup(): void {
|
private _setup(): void {
|
||||||
this._parent = document ? document.body : null;
|
this._parent = document ? document.body : null;
|
||||||
|
|
||||||
this.cols = Math.max(this.options.cols, MINIMUM_COLS);
|
|
||||||
this.rows = Math.max(this.options.rows, MINIMUM_ROWS);
|
|
||||||
|
|
||||||
this.cursorState = 0;
|
this.cursorState = 0;
|
||||||
this.cursorHidden = false;
|
this.cursorHidden = false;
|
||||||
this._customKeyEventHandler = null;
|
this._customKeyEventHandler = null;
|
||||||
@@ -313,7 +313,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
this.soundManager = this.soundManager || new SoundManager(this);
|
this.soundManager = this.soundManager || new SoundManager(this);
|
||||||
|
|
||||||
// Create the terminal's buffers and set the current buffer
|
// Create the terminal's buffers and set the current buffer
|
||||||
this.buffers = new BufferSet(this);
|
this.buffers = new BufferSet(this.optionsService, this._bufferService);
|
||||||
if (this.selectionManager) {
|
if (this.selectionManager) {
|
||||||
this.selectionManager.clearSelection();
|
this.selectionManager.clearSelection();
|
||||||
this.selectionManager.initBuffersListeners();
|
this.selectionManager.initBuffersListeners();
|
||||||
@@ -643,7 +643,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
this.register(this.addDisposableListener('focus', () => this._renderCoordinator.onFocus()));
|
this.register(this.addDisposableListener('focus', () => this._renderCoordinator.onFocus()));
|
||||||
this.register(this._renderCoordinator.onDimensionsChange(() => this.viewport.syncScrollArea()));
|
this.register(this._renderCoordinator.onDimensionsChange(() => this.viewport.syncScrollArea()));
|
||||||
|
|
||||||
this.selectionManager = new SelectionManager(this, this._charSizeService);
|
this.selectionManager = new SelectionManager(this, this._charSizeService, this._bufferService);
|
||||||
this.register(this.selectionManager.onSelectionChange(() => this._onSelectionChange.fire()));
|
this.register(this.selectionManager.onSelectionChange(() => this._onSelectionChange.fire()));
|
||||||
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this.selectionManager.onMouseDown(e)));
|
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this.selectionManager.onMouseDown(e)));
|
||||||
this.register(this.selectionManager.onRedrawRequest(e => this._renderCoordinator.onSelectionChanged(e.start, e.end, e.columnSelectMode)));
|
this.register(this.selectionManager.onRedrawRequest(e => this._renderCoordinator.onSelectionChanged(e.start, e.end, e.columnSelectMode)));
|
||||||
@@ -1745,8 +1745,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
|
|
||||||
this.buffers.resize(x, y);
|
this.buffers.resize(x, y);
|
||||||
|
|
||||||
this.cols = x;
|
this._bufferService.resize(x, y);
|
||||||
this.rows = y;
|
|
||||||
this.buffers.setupTabStops(this.cols);
|
this.buffers.setupTabStops(this.cols);
|
||||||
|
|
||||||
if (this._charSizeService) {
|
if (this._charSizeService) {
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { IRenderer, IRenderDimensions } from './renderer/Types';
|
import { IRenderer, IRenderDimensions } from './renderer/Types';
|
||||||
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminal, IBuffer, IBufferSet, IBrowser, ISelectionManager, ITerminalOptions, ILinkifier, IMouseHelper, ILinkMatcherOptions, CharacterJoinerHandler, IBufferStringIterator } from './Types';
|
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminal, IBrowser, ISelectionManager, ITerminalOptions, ILinkifier, IMouseHelper, ILinkMatcherOptions, CharacterJoinerHandler } from './Types';
|
||||||
|
import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types';
|
||||||
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener } from 'common/Types';
|
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener } from 'common/Types';
|
||||||
import { Buffer } from './Buffer';
|
import { Buffer } from 'common/buffer/Buffer';
|
||||||
import * as Browser from 'common/Platform';
|
import * as Browser from 'common/Platform';
|
||||||
import { IDisposable, IMarker, IEvent, ISelectionPosition } from 'xterm';
|
import { IDisposable, IMarker, IEvent, ISelectionPosition } from 'xterm';
|
||||||
import { Terminal } from './Terminal';
|
import { Terminal } from './Terminal';
|
||||||
@@ -30,8 +31,7 @@ export class MockTerminal implements ITerminal {
|
|||||||
onTitleChange: IEvent<string>;
|
onTitleChange: IEvent<string>;
|
||||||
onScroll: IEvent<number>;
|
onScroll: IEvent<number>;
|
||||||
onKey: IEvent<{ key: string; domEvent: KeyboardEvent; }>;
|
onKey: IEvent<{ key: string; domEvent: KeyboardEvent; }>;
|
||||||
onRender: IEvent<{ start: number
|
onRender: IEvent<{ start: number; end: number; }>;
|
||||||
; end: number; }>;
|
|
||||||
onResize: IEvent<{ cols: number; rows: number; }>;
|
onResize: IEvent<{ cols: number; rows: number; }>;
|
||||||
markers: IMarker[];
|
markers: IMarker[];
|
||||||
optionsService: IOptionsService;
|
optionsService: IOptionsService;
|
||||||
|
|||||||
+2
-50
@@ -4,10 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ITerminalOptions as IPublicTerminalOptions, IEventEmitter, IDisposable, IMarker, ISelectionPosition } from 'xterm';
|
import { ITerminalOptions as IPublicTerminalOptions, IEventEmitter, IDisposable, IMarker, ISelectionPosition } from 'xterm';
|
||||||
import { ICharset, IAttributeData, ICellData, IBufferLine, CharData, ICircularList } from 'common/Types';
|
import { ICharset, IAttributeData, CharData } from 'common/Types';
|
||||||
import { IEvent } from 'common/EventEmitter2';
|
import { IEvent } from 'common/EventEmitter2';
|
||||||
import { IColorSet } from 'browser/Types';
|
import { IColorSet } from 'browser/Types';
|
||||||
import { IOptionsService } from 'common/services/Services';
|
import { IOptionsService } from 'common/services/Services';
|
||||||
|
import { IBuffer, IBufferSet } from 'common/buffer/Types';
|
||||||
|
|
||||||
export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
|
export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
|
||||||
|
|
||||||
@@ -18,9 +19,6 @@ export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: bo
|
|||||||
|
|
||||||
export type CharacterJoinerHandler = (text: string) => [number, number][];
|
export type CharacterJoinerHandler = (text: string) => [number, number][];
|
||||||
|
|
||||||
// BufferIndex denotes a position in the buffer: [rowIndex, colIndex]
|
|
||||||
export type BufferIndex = [number, number];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface encapsulates everything needed from the Terminal by the
|
* This interface encapsulates everything needed from the Terminal by the
|
||||||
* InputHandler. This cleanly separates the large amount of methods needed by
|
* InputHandler. This cleanly separates the large amount of methods needed by
|
||||||
@@ -298,52 +296,6 @@ export interface ITerminalOptions extends IPublicTerminalOptions {
|
|||||||
useFlowControl?: boolean;
|
useFlowControl?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IBufferStringIteratorResult {
|
|
||||||
range: {first: number, last: number};
|
|
||||||
content: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IBufferStringIterator {
|
|
||||||
hasNext(): boolean;
|
|
||||||
next(): IBufferStringIteratorResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IBuffer {
|
|
||||||
readonly lines: ICircularList<IBufferLine>;
|
|
||||||
ydisp: number;
|
|
||||||
ybase: number;
|
|
||||||
y: number;
|
|
||||||
x: number;
|
|
||||||
tabs: any;
|
|
||||||
scrollBottom: number;
|
|
||||||
scrollTop: number;
|
|
||||||
hasScrollback: boolean;
|
|
||||||
savedY: number;
|
|
||||||
savedX: number;
|
|
||||||
savedCurAttrData: IAttributeData;
|
|
||||||
isCursorInViewport: boolean;
|
|
||||||
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string;
|
|
||||||
getWrappedRangeForLine(y: number): { first: number, last: number };
|
|
||||||
nextStop(x?: number): number;
|
|
||||||
prevStop(x?: number): number;
|
|
||||||
getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine;
|
|
||||||
stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[];
|
|
||||||
iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator;
|
|
||||||
getNullCell(attr?: IAttributeData): ICellData;
|
|
||||||
getWhitespaceCell(attr?: IAttributeData): ICellData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IBufferSet {
|
|
||||||
alt: IBuffer;
|
|
||||||
normal: IBuffer;
|
|
||||||
active: IBuffer;
|
|
||||||
|
|
||||||
onBufferActivate: IEvent<{ activeBuffer: IBuffer, inactiveBuffer: IBuffer }>;
|
|
||||||
|
|
||||||
activateNormalBuffer(): void;
|
|
||||||
activateAltBuffer(fillAttr?: IAttributeData): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ISelectionManager {
|
export interface ISelectionManager {
|
||||||
selectionText: string;
|
selectionText: string;
|
||||||
selectionStart: [number, number];
|
selectionStart: [number, number];
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class DomMeasureStrategy implements IMeasureStrategy {
|
|||||||
|
|
||||||
// Note that this triggers a synchronous layout
|
// Note that this triggers a synchronous layout
|
||||||
const geometry = this._measureElement.getBoundingClientRect();
|
const geometry = this._measureElement.getBoundingClientRect();
|
||||||
console.log('measure', geometry);
|
|
||||||
// If values are 0 then the element is likely currently display:none, in which case we should
|
// If values are 0 then the element is likely currently display:none, in which case we should
|
||||||
// retain the previous value.
|
// retain the previous value.
|
||||||
if (geometry.width !== 0 && geometry.height !== 0) {
|
if (geometry.width !== 0 && geometry.height !== 0) {
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { IBufferService, IOptionsService, ITerminalOptions, IPartialTerminalOptions } from 'common/services/Services';
|
||||||
|
import { IEvent, EventEmitter2 } from 'common/EventEmitter2';
|
||||||
|
import { clone } from 'common/Clone';
|
||||||
|
import { DEFAULT_OPTIONS } from 'common/services/OptionsService';
|
||||||
|
|
||||||
|
export class MockBufferService implements IBufferService {
|
||||||
|
constructor(
|
||||||
|
public cols: number,
|
||||||
|
public rows: number
|
||||||
|
) {}
|
||||||
|
resize(cols: number, rows: number): void {
|
||||||
|
this.cols = cols;
|
||||||
|
this.rows = rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MockOptionsService implements IOptionsService {
|
||||||
|
options: ITerminalOptions = clone(DEFAULT_OPTIONS);
|
||||||
|
onOptionChange: IEvent<string> = new EventEmitter2<string>().event;
|
||||||
|
constructor(testOptions: IPartialTerminalOptions) {
|
||||||
|
Object.keys(testOptions).forEach(key => this.options[key] = (<any>testOptions)[key]);
|
||||||
|
}
|
||||||
|
setOption<T>(key: string, value: T): void {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
getOption<T>(key: string): T {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -4,11 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CircularList, IInsertEvent } from 'common/CircularList';
|
import { CircularList, IInsertEvent } from 'common/CircularList';
|
||||||
import { ITerminal, IBuffer, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult } from './Types';
|
import { IBuffer, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult } from 'common/buffer/Types';
|
||||||
import { IBufferLine, ICellData, IAttributeData } from 'common/Types';
|
import { IBufferLine, ICellData, IAttributeData } from 'common/Types';
|
||||||
import { BufferLine, CellData, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
import { BufferLine, CellData, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||||
import { reflowLargerApplyNewLayout, reflowLargerCreateNewLayout, reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths, getWrappedLineTrimmedLength } from 'common/buffer/BufferReflow';
|
import { reflowLargerApplyNewLayout, reflowLargerCreateNewLayout, reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths, getWrappedLineTrimmedLength } from 'common/buffer/BufferReflow';
|
||||||
import { Marker } from 'common/buffer/Marker';
|
import { Marker } from 'common/buffer/Marker';
|
||||||
|
import { IOptionsService, IBufferService } from 'common/services/Services';
|
||||||
|
|
||||||
export const MAX_BUFFER_SIZE = 4294967295; // 2^32 - 1
|
export const MAX_BUFFER_SIZE = 4294967295; // 2^32 - 1
|
||||||
|
|
||||||
@@ -21,15 +22,16 @@ export const MAX_BUFFER_SIZE = 4294967295; // 2^32 - 1
|
|||||||
*/
|
*/
|
||||||
export class Buffer implements IBuffer {
|
export class Buffer implements IBuffer {
|
||||||
public lines: CircularList<IBufferLine>;
|
public lines: CircularList<IBufferLine>;
|
||||||
public ydisp: number;
|
public ydisp: number = 0;
|
||||||
public ybase: number;
|
public ybase: number = 0;
|
||||||
public y: number;
|
public y: number = 0;
|
||||||
public x: number;
|
public x: number = 0;
|
||||||
public scrollBottom: number;
|
public scrollBottom: number;
|
||||||
public scrollTop: number;
|
public scrollTop: number;
|
||||||
|
// TODO: Type me
|
||||||
public tabs: any;
|
public tabs: any;
|
||||||
public savedY: number;
|
public savedY: number = 0;
|
||||||
public savedX: number;
|
public savedX: number = 0;
|
||||||
public savedCurAttrData = DEFAULT_ATTR_DATA.clone();
|
public savedCurAttrData = DEFAULT_ATTR_DATA.clone();
|
||||||
public markers: Marker[] = [];
|
public markers: Marker[] = [];
|
||||||
private _nullCell: ICellData = CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
private _nullCell: ICellData = CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||||
@@ -37,19 +39,17 @@ export class Buffer implements IBuffer {
|
|||||||
private _cols: number;
|
private _cols: number;
|
||||||
private _rows: number;
|
private _rows: number;
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new Buffer.
|
|
||||||
* @param _terminal The terminal the Buffer will belong to.
|
|
||||||
* @param _hasScrollback Whether the buffer should respect the scrollback of
|
|
||||||
* the terminal.
|
|
||||||
*/
|
|
||||||
constructor(
|
constructor(
|
||||||
private _terminal: ITerminal,
|
private _hasScrollback: boolean,
|
||||||
private _hasScrollback: boolean
|
private _optionsService: IOptionsService,
|
||||||
|
private _bufferService: IBufferService
|
||||||
) {
|
) {
|
||||||
this._cols = this._terminal.cols;
|
this._cols = this._bufferService.cols;
|
||||||
this._rows = this._terminal.rows;
|
this._rows = this._bufferService.rows;
|
||||||
this.clear();
|
this.lines = new CircularList<IBufferLine>(this._getCorrectBufferLength(this._rows));
|
||||||
|
this.scrollTop = 0;
|
||||||
|
this.scrollBottom = this._rows - 1;
|
||||||
|
this.setupTabStops();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getNullCell(attr?: IAttributeData): ICellData {
|
public getNullCell(attr?: IAttributeData): ICellData {
|
||||||
@@ -75,7 +75,7 @@ export class Buffer implements IBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine {
|
public getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine {
|
||||||
return new BufferLine(this._terminal.cols, this.getNullCell(attr), isWrapped);
|
return new BufferLine(this._bufferService.cols, this.getNullCell(attr), isWrapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get hasScrollback(): boolean {
|
public get hasScrollback(): boolean {
|
||||||
@@ -98,7 +98,7 @@ export class Buffer implements IBuffer {
|
|||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
const correctBufferLength = rows + this._terminal.options.scrollback;
|
const correctBufferLength = rows + this._optionsService.options.scrollback;
|
||||||
|
|
||||||
return correctBufferLength > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : correctBufferLength;
|
return correctBufferLength > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : correctBufferLength;
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ export class Buffer implements IBuffer {
|
|||||||
// Deal with columns increasing (reducing needs to happen after reflow)
|
// Deal with columns increasing (reducing needs to happen after reflow)
|
||||||
if (this._cols < newCols) {
|
if (this._cols < newCols) {
|
||||||
for (let i = 0; i < this.lines.length; i++) {
|
for (let i = 0; i < this.lines.length; i++) {
|
||||||
this.lines.get(i).resize(newCols, nullCell);
|
this.lines.get(i)!.resize(newCols, nullCell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ export class Buffer implements IBuffer {
|
|||||||
// Trim the end of the line off if cols shrunk
|
// Trim the end of the line off if cols shrunk
|
||||||
if (this._cols > newCols) {
|
if (this._cols > newCols) {
|
||||||
for (let i = 0; i < this.lines.length; i++) {
|
for (let i = 0; i < this.lines.length; i++) {
|
||||||
this.lines.get(i).resize(newCols, nullCell);
|
this.lines.get(i)!.resize(newCols, nullCell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -237,7 +237,7 @@ export class Buffer implements IBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private get _isReflowEnabled(): boolean {
|
private get _isReflowEnabled(): boolean {
|
||||||
return this._hasScrollback && !this._terminal.options.windowsMode;
|
return this._hasScrollback && !this._optionsService.options.windowsMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _reflow(newCols: number, newRows: number): void {
|
private _reflow(newCols: number, newRows: number): void {
|
||||||
@@ -509,11 +509,11 @@ export class Buffer implements IBuffer {
|
|||||||
let first = y;
|
let first = y;
|
||||||
let last = y;
|
let last = y;
|
||||||
// Scan upwards for wrapped lines
|
// Scan upwards for wrapped lines
|
||||||
while (first > 0 && this.lines.get(first).isWrapped) {
|
while (first > 0 && this.lines.get(first)!.isWrapped) {
|
||||||
first--;
|
first--;
|
||||||
}
|
}
|
||||||
// Scan downwards for wrapped lines
|
// Scan downwards for wrapped lines
|
||||||
while (last + 1 < this.lines.length && this.lines.get(last + 1).isWrapped) {
|
while (last + 1 < this.lines.length && this.lines.get(last + 1)!.isWrapped) {
|
||||||
last++;
|
last++;
|
||||||
}
|
}
|
||||||
return { first, last };
|
return { first, last };
|
||||||
@@ -533,7 +533,7 @@ export class Buffer implements IBuffer {
|
|||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; i < this._cols; i += this._terminal.options.tabStopWidth) {
|
for (; i < this._cols; i += this._optionsService.options.tabStopWidth) {
|
||||||
this.tabs[i] = true;
|
this.tabs[i] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,21 +4,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { ITerminal } from './Types';
|
import { BufferSet } from 'common/buffer/BufferSet';
|
||||||
import { BufferSet } from './BufferSet';
|
import { Buffer } from 'common/buffer/Buffer';
|
||||||
import { Buffer } from './Buffer';
|
import { MockOptionsService, MockBufferService } from 'common/TestUtils.test';
|
||||||
import { MockTerminal } from './TestUtils.test';
|
|
||||||
|
|
||||||
describe('BufferSet', () => {
|
describe('BufferSet', () => {
|
||||||
let terminal: ITerminal;
|
|
||||||
let bufferSet: BufferSet;
|
let bufferSet: BufferSet;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
terminal = new MockTerminal();
|
bufferSet = new BufferSet(
|
||||||
(terminal as any).cols = 80;
|
new MockOptionsService({ scrollback: 1000 }),
|
||||||
(terminal as any).rows = 24;
|
new MockBufferService(80, 24)
|
||||||
terminal.options.scrollback = 1000;
|
);
|
||||||
bufferSet = new BufferSet(terminal);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('constructor', () => {
|
describe('constructor', () => {
|
||||||
@@ -3,10 +3,11 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ITerminal, IBufferSet, IBuffer } from './Types';
|
import { IBuffer, IBufferSet } from 'common/buffer/Types';
|
||||||
import { IAttributeData } from 'common/Types';
|
import { IAttributeData } from 'common/Types';
|
||||||
import { Buffer } from './Buffer';
|
import { Buffer } from 'common/buffer/Buffer';
|
||||||
import { EventEmitter2, IEvent } from 'common/EventEmitter2';
|
import { EventEmitter2, IEvent } from 'common/EventEmitter2';
|
||||||
|
import { IOptionsService, IBufferService } from 'common/services/Services';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The BufferSet represents the set of two buffers used by xterm terminals (normal and alt) and
|
* The BufferSet represents the set of two buffers used by xterm terminals (normal and alt) and
|
||||||
@@ -25,13 +26,16 @@ export class BufferSet implements IBufferSet {
|
|||||||
* Create a new BufferSet for the given terminal.
|
* Create a new BufferSet for the given terminal.
|
||||||
* @param _terminal - The terminal the BufferSet will belong to
|
* @param _terminal - The terminal the BufferSet will belong to
|
||||||
*/
|
*/
|
||||||
constructor(private _terminal: ITerminal) {
|
constructor(
|
||||||
this._normal = new Buffer(this._terminal, true);
|
readonly optionsService: IOptionsService,
|
||||||
|
readonly bufferService: IBufferService
|
||||||
|
) {
|
||||||
|
this._normal = new Buffer(true, optionsService, bufferService);
|
||||||
this._normal.fillViewportRows();
|
this._normal.fillViewportRows();
|
||||||
|
|
||||||
// The alt buffer should never have scrollback.
|
// The alt buffer should never have scrollback.
|
||||||
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||||
this._alt = new Buffer(this._terminal, false);
|
this._alt = new Buffer(false, optionsService, bufferService);
|
||||||
this._activeBuffer = this._normal;
|
this._activeBuffer = this._normal;
|
||||||
|
|
||||||
this.setupTabStops();
|
this.setupTabStops();
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { IAttributeData, ICircularList, IBufferLine, ICellData } from 'common/Types';
|
||||||
|
import { IEvent } from 'common/EventEmitter2';
|
||||||
|
|
||||||
|
// BufferIndex denotes a position in the buffer: [rowIndex, colIndex]
|
||||||
|
export type BufferIndex = [number, number];
|
||||||
|
|
||||||
|
export interface IBufferStringIteratorResult {
|
||||||
|
range: {first: number, last: number};
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IBufferStringIterator {
|
||||||
|
hasNext(): boolean;
|
||||||
|
next(): IBufferStringIteratorResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IBuffer {
|
||||||
|
readonly lines: ICircularList<IBufferLine>;
|
||||||
|
ydisp: number;
|
||||||
|
ybase: number;
|
||||||
|
y: number;
|
||||||
|
x: number;
|
||||||
|
tabs: any;
|
||||||
|
scrollBottom: number;
|
||||||
|
scrollTop: number;
|
||||||
|
hasScrollback: boolean;
|
||||||
|
savedY: number;
|
||||||
|
savedX: number;
|
||||||
|
savedCurAttrData: IAttributeData;
|
||||||
|
isCursorInViewport: boolean;
|
||||||
|
translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string;
|
||||||
|
getWrappedRangeForLine(y: number): { first: number, last: number };
|
||||||
|
nextStop(x?: number): number;
|
||||||
|
prevStop(x?: number): number;
|
||||||
|
getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine;
|
||||||
|
stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[];
|
||||||
|
iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator;
|
||||||
|
getNullCell(attr?: IAttributeData): ICellData;
|
||||||
|
getWhitespaceCell(attr?: IAttributeData): ICellData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IBufferSet {
|
||||||
|
alt: IBuffer;
|
||||||
|
normal: IBuffer;
|
||||||
|
active: IBuffer;
|
||||||
|
|
||||||
|
onBufferActivate: IEvent<{ activeBuffer: IBuffer, inactiveBuffer: IBuffer }>;
|
||||||
|
|
||||||
|
activateNormalBuffer(): void;
|
||||||
|
activateAltBuffer(fillAttr?: IAttributeData): void;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { IBufferService, IOptionsService } from './Services';
|
||||||
|
|
||||||
|
export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars
|
||||||
|
export const MINIMUM_ROWS = 1;
|
||||||
|
|
||||||
|
export class BufferService implements IBufferService {
|
||||||
|
public cols: number;
|
||||||
|
public rows: number;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
optionsService: IOptionsService
|
||||||
|
) {
|
||||||
|
this.cols = Math.max(optionsService.options.cols, MINIMUM_COLS);
|
||||||
|
this.rows = Math.max(optionsService.options.rows, MINIMUM_ROWS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public resize(cols: number, rows: number): void {
|
||||||
|
this.cols = cols;
|
||||||
|
this.rows = rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ import { clone } from 'common/Clone';
|
|||||||
export const DEFAULT_BELL_SOUND = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg==';
|
export const DEFAULT_BELL_SOUND = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg==';
|
||||||
|
|
||||||
// TODO: Freeze?
|
// TODO: Freeze?
|
||||||
const DEFAULT_OPTIONS: ITerminalOptions = {
|
export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
|
||||||
cols: 80,
|
cols: 80,
|
||||||
rows: 24,
|
rows: 24,
|
||||||
cursorBlink: false,
|
cursorBlink: false,
|
||||||
@@ -47,7 +47,7 @@ const DEFAULT_OPTIONS: ITerminalOptions = {
|
|||||||
debug: false,
|
debug: false,
|
||||||
cancelEvents: false,
|
cancelEvents: false,
|
||||||
useFlowControl: false
|
useFlowControl: false
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The set of options that only have an effect when set in the Terminal constructor.
|
* The set of options that only have an effect when set in the Terminal constructor.
|
||||||
|
|||||||
Vendored
+9
@@ -5,6 +5,15 @@
|
|||||||
|
|
||||||
import { IEvent } from 'common/EventEmitter2';
|
import { IEvent } from 'common/EventEmitter2';
|
||||||
|
|
||||||
|
export interface IBufferService {
|
||||||
|
readonly cols: number;
|
||||||
|
readonly rows: number;
|
||||||
|
|
||||||
|
// TODO: Move resize event here
|
||||||
|
|
||||||
|
resize(cols: number, rows: number): void;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IOptionsService {
|
export interface IOptionsService {
|
||||||
readonly options: ITerminalOptions;
|
readonly options: ITerminalOptions;
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBuffer as IBufferApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi } from 'xterm';
|
import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBuffer as IBufferApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi } from 'xterm';
|
||||||
import { ITerminal, IBuffer } from '../Types';
|
import { ITerminal } from '../Types';
|
||||||
import { IBufferLine } from 'common/Types';
|
import { IBufferLine } from 'common/Types';
|
||||||
|
import { IBuffer } from 'common/buffer/Types';
|
||||||
import { Terminal as TerminalCore } from '../Terminal';
|
import { Terminal as TerminalCore } from '../Terminal';
|
||||||
import * as Strings from '../Strings';
|
import * as Strings from '../Strings';
|
||||||
import { IEvent } from 'common/EventEmitter2';
|
import { IEvent } from 'common/EventEmitter2';
|
||||||
|
|||||||
+1
-1
@@ -95,7 +95,7 @@
|
|||||||
{"type": "default", "format": "camelCase", "leadingUnderscore": "forbid"},
|
{"type": "default", "format": "camelCase", "leadingUnderscore": "forbid"},
|
||||||
{"type": "type", "format": "PascalCase"},
|
{"type": "type", "format": "PascalCase"},
|
||||||
{"type": "class", "format": "PascalCase"},
|
{"type": "class", "format": "PascalCase"},
|
||||||
{"type": "property", "modifiers": ["const"], "format": "UPPER_CASE"},
|
{"type": "property", "modifiers": ["const"], "format": ["camelCase", "UPPER_CASE"]},
|
||||||
{"type": "member", "modifiers": ["protected"], "format": "camelCase", "leadingUnderscore": "allow"},
|
{"type": "member", "modifiers": ["protected"], "format": "camelCase", "leadingUnderscore": "allow"},
|
||||||
// TODO: Change allow to require when there aren't many PRs out
|
// TODO: Change allow to require when there aren't many PRs out
|
||||||
// {"type": "member", "modifiers": ["protected"], "format": "camelCase", "leadingUnderscore": "require"},
|
// {"type": "member", "modifiers": ["protected"], "format": "camelCase", "leadingUnderscore": "require"},
|
||||||
|
|||||||
Reference in New Issue
Block a user