Merge branch 'master' into mouse_modes

This commit is contained in:
jerch
2019-07-16 22:06:14 +02:00
committed by GitHub
32 changed files with 1104 additions and 735 deletions
+6 -6
View File
@@ -11,7 +11,7 @@
"prepackage": "npm run build",
"package": "webpack",
"start": "node demo/start",
"lint": "tslint 'src/**/*.ts' './demo/**/*.ts' './addons/**/*.ts'",
"lint": "tslint 'src/**/*.ts' 'addons/**/*.ts'",
"test": "npm run test-unit",
"posttest": "npm run lint",
"test-api": "mocha \"**/*.api.js\"",
@@ -36,20 +36,20 @@
"@types/webpack": "^4.4.11",
"@types/ws": "^6.0.1",
"chai": "3.5.0",
"express": "4.13.4",
"express-ws": "2.0.0-rc.1",
"express": "^4.17.1",
"express-ws": "^4.0.0",
"glob": "^7.0.5",
"jsdom": "^11.11.0",
"mocha": "^6.1.4",
"node-pty": "0.7.6",
"puppeteer": "^1.15.0",
"source-map-loader": "^0.2.4",
"ts-loader": "^4.5.0",
"tslint": "^5.9.1",
"ts-loader": "^6.0.4",
"tslint": "^5.18.0",
"tslint-consistent-codestyle": "^1.13.0",
"typescript": "3.5",
"utf8": "^3.0.0",
"webpack": "^4.17.1",
"webpack": "^4.35.3",
"webpack-cli": "^3.1.0",
"ws": "^7.0.0",
"xterm-benchmark": "^0.1.3"
+6 -10
View File
@@ -11,7 +11,7 @@ import { RenderDebouncer } from 'browser/RenderDebouncer';
import { addDisposableDomListener } from 'browser/Lifecycle';
import { Disposable } from 'common/Lifecycle';
import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
import { IRenderDimensions } from 'browser/renderer/Types';
import { IRenderService } from 'browser/services/Services';
const MAX_ROWS_TO_READ = 20;
@@ -47,8 +47,8 @@ export class AccessibilityManager extends Disposable {
private _charsToAnnounce: string = '';
constructor(
private _terminal: ITerminal,
private _dimensions: IRenderDimensions
private readonly _terminal: ITerminal,
private readonly _renderService: IRenderService
) {
super();
this._accessibilityTreeRoot = document.createElement('div');
@@ -90,6 +90,7 @@ export class AccessibilityManager extends Disposable {
this.register(this._terminal.onA11yTab(spaceCount => this._onTab(spaceCount)));
this.register(this._terminal.onKey(e => this._onKey(e.key)));
this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
this._screenDprMonitor = new ScreenDprMonitor();
this.register(this._screenDprMonitor);
@@ -271,7 +272,7 @@ export class AccessibilityManager extends Disposable {
}
private _refreshRowsDimensions(): void {
if (!this._dimensions.actualCellHeight) {
if (!this._renderService.dimensions.actualCellHeight) {
return;
}
if (this._rowElements.length !== this._terminal.rows) {
@@ -282,13 +283,8 @@ export class AccessibilityManager extends Disposable {
}
}
public setDimensions(dimensions: IRenderDimensions): void {
this._dimensions = dimensions;
this._refreshRowsDimensions();
}
private _refreshRowDimensions(element: HTMLElement): void {
element.style.height = `${this._dimensions.actualCellHeight}px`;
element.style.height = `${this._renderService.dimensions.actualCellHeight}px`;
}
private _announceCharacters(): void {
+33 -31
View File
@@ -13,8 +13,10 @@ import { CellData } from 'common/buffer/CellData';
import { Attributes } from 'common/buffer/Constants';
import { AttributeData } from 'common/buffer/AttributeData';
import { Params } from 'common/parser/Params';
import { MockCoreService, MockBufferService, MockOptionsService, MockLogService } from 'common/TestUtils.test';
import { MockCoreService, MockBufferService, MockDirtyRowService, MockOptionsService, MockLogService } from 'common/TestUtils.test';
import { IBufferService } from 'common/services/Services';
import { DEFAULT_OPTIONS } from 'common/services/OptionsService';
import { clone } from 'common/Clone';
function getCursor(term: TestTerminal): number[] {
return [
@@ -31,7 +33,7 @@ describe('InputHandler', () => {
bufferService.buffer.x = 1;
bufferService.buffer.y = 2;
bufferService.buffer.ybase = 0;
const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService());
const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService());
// Save cursor position
inputHandler.saveCursor();
assert.equal(bufferService.buffer.x, 1);
@@ -49,43 +51,43 @@ describe('InputHandler', () => {
});
describe('setCursorStyle', () => {
it('should call Terminal.setOption with correct params', () => {
const terminal = new MockInputHandlingTerminal();
const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService());
const optionsService = new MockOptionsService();
const inputHandler = new InputHandler(new MockInputHandlingTerminal(), new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), optionsService);
const collect = ' ';
inputHandler.setCursorStyle(Params.fromArray([0]), collect);
assert.equal(terminal.options['cursorStyle'], 'block');
assert.equal(terminal.options['cursorBlink'], true);
assert.equal(optionsService.options['cursorStyle'], 'block');
assert.equal(optionsService.options['cursorBlink'], true);
terminal.options = {};
optionsService.options = clone(DEFAULT_OPTIONS);
inputHandler.setCursorStyle(Params.fromArray([1]), collect);
assert.equal(terminal.options['cursorStyle'], 'block');
assert.equal(terminal.options['cursorBlink'], true);
assert.equal(optionsService.options['cursorStyle'], 'block');
assert.equal(optionsService.options['cursorBlink'], true);
terminal.options = {};
optionsService.options = clone(DEFAULT_OPTIONS);
inputHandler.setCursorStyle(Params.fromArray([2]), collect);
assert.equal(terminal.options['cursorStyle'], 'block');
assert.equal(terminal.options['cursorBlink'], false);
assert.equal(optionsService.options['cursorStyle'], 'block');
assert.equal(optionsService.options['cursorBlink'], false);
terminal.options = {};
optionsService.options = clone(DEFAULT_OPTIONS);
inputHandler.setCursorStyle(Params.fromArray([3]), collect);
assert.equal(terminal.options['cursorStyle'], 'underline');
assert.equal(terminal.options['cursorBlink'], true);
assert.equal(optionsService.options['cursorStyle'], 'underline');
assert.equal(optionsService.options['cursorBlink'], true);
terminal.options = {};
optionsService.options = clone(DEFAULT_OPTIONS);
inputHandler.setCursorStyle(Params.fromArray([4]), collect);
assert.equal(terminal.options['cursorStyle'], 'underline');
assert.equal(terminal.options['cursorBlink'], false);
assert.equal(optionsService.options['cursorStyle'], 'underline');
assert.equal(optionsService.options['cursorBlink'], false);
terminal.options = {};
optionsService.options = clone(DEFAULT_OPTIONS);
inputHandler.setCursorStyle(Params.fromArray([5]), collect);
assert.equal(terminal.options['cursorStyle'], 'bar');
assert.equal(terminal.options['cursorBlink'], true);
assert.equal(optionsService.options['cursorStyle'], 'bar');
assert.equal(optionsService.options['cursorBlink'], true);
terminal.options = {};
optionsService.options = clone(DEFAULT_OPTIONS);
inputHandler.setCursorStyle(Params.fromArray([6]), collect);
assert.equal(terminal.options['cursorStyle'], 'bar');
assert.equal(terminal.options['cursorBlink'], false);
assert.equal(optionsService.options['cursorStyle'], 'bar');
assert.equal(optionsService.options['cursorBlink'], false);
});
});
describe('setMode', () => {
@@ -93,7 +95,7 @@ describe('InputHandler', () => {
const terminal = new MockInputHandlingTerminal();
const collect = '?';
terminal.bracketedPasteMode = false;
const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService());
const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService());
// Set bracketed paste mode
inputHandler.setMode(Params.fromArray([2004]), collect);
assert.equal(terminal.bracketedPasteMode, true);
@@ -112,7 +114,7 @@ describe('InputHandler', () => {
it('insertChars', function(): void {
const term = new Terminal();
const bufferService = new MockBufferService(80, 30);
const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService());
const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService());
// insert some data in first and second line
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
@@ -150,7 +152,7 @@ describe('InputHandler', () => {
it('deleteChars', function(): void {
const term = new Terminal();
const bufferService = new MockBufferService(80, 30);
const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService());
const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService());
// insert some data in first and second line
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
@@ -191,7 +193,7 @@ describe('InputHandler', () => {
it('eraseInLine', function(): void {
const term = new Terminal();
const bufferService = new MockBufferService(80, 30);
const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService());
const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService());
// fill 6 lines to test 3 different states
inputHandler.parse(Array(bufferService.cols + 1).join('a'));
@@ -220,7 +222,7 @@ describe('InputHandler', () => {
it('eraseInDisplay', function(): void {
const term = new Terminal({cols: 80, rows: 7});
const bufferService = new MockBufferService(80, 7);
const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService());
const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService());
// fill display with a's
for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
@@ -355,7 +357,7 @@ describe('InputHandler', () => {
describe('print', () => {
it('should not cause an infinite loop (regression test)', () => {
const term = new Terminal();
const inputHandler = new InputHandler(term, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService());
const inputHandler = new InputHandler(term, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService());
const container = new Uint32Array(10);
container[0] = 0x200B;
inputHandler.print(container, 0, 1);
@@ -370,7 +372,7 @@ describe('InputHandler', () => {
beforeEach(() => {
term = new Terminal();
bufferService = new MockBufferService(80, 30);
handler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService());
handler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService());
});
it('should handle DECSET/DECRST 47 (alt screen buffer)', () => {
handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST');
+33 -53
View File
@@ -19,7 +19,7 @@ import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content
import { CellData } from 'common/buffer/CellData';
import { AttributeData } from 'common/buffer/AttributeData';
import { IAttributeData, IDisposable } from 'common/Types';
import { ICoreService, IBufferService, IOptionsService, ILogService } from 'common/services/Services';
import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService } from 'common/services/Services';
import { ISelectionService } from 'browser/services/Services';
/**
@@ -128,12 +128,13 @@ export class InputHandler extends Disposable implements IInputHandler {
public get onScroll(): IEvent<number> { return this._onScroll.event; }
constructor(
protected _terminal: IInputHandlingTerminal,
private _bufferService: IBufferService,
private _coreService: ICoreService,
private _logService: ILogService,
private _optionsService: IOptionsService,
private _parser: IEscapeSequenceParser = new EscapeSequenceParser())
protected _terminal: IInputHandlingTerminal,
private readonly _bufferService: IBufferService,
private readonly _coreService: ICoreService,
private readonly _dirtyRowService: IDirtyRowService,
private readonly _logService: ILogService,
private readonly _optionsService: IOptionsService,
private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser())
{
super();
@@ -306,7 +307,6 @@ export class InputHandler extends Disposable implements IInputHandler {
public dispose(): void {
super.dispose();
this._terminal = null;
}
// TODO: When InputHandler moves into common, browser dependencies need to move out
@@ -315,11 +315,6 @@ export class InputHandler extends Disposable implements IInputHandler {
}
public parse(data: string): void {
// Ensure the terminal is not disposed
if (!this._terminal) {
return;
}
let buffer = this._bufferService.buffer;
const cursorStartX = buffer.x;
const cursorStartY = buffer.y;
@@ -338,11 +333,6 @@ export class InputHandler extends Disposable implements IInputHandler {
}
public parseUtf8(data: Uint8Array): void {
// Ensure the terminal is not disposed
if (!this._terminal) {
return;
}
let buffer = this._bufferService.buffer;
const cursorStartX = buffer.x;
const cursorStartY = buffer.y;
@@ -365,14 +355,14 @@ export class InputHandler extends Disposable implements IInputHandler {
let chWidth: number;
const buffer = this._bufferService.buffer;
const charset = this._terminal.charset;
const screenReaderMode = this._terminal.options.screenReaderMode;
const screenReaderMode = this._optionsService.options.screenReaderMode;
const cols = this._bufferService.cols;
const wraparoundMode = this._terminal.wraparoundMode;
const insertMode = this._terminal.insertMode;
const curAttr = this._terminal.curAttrData;
let bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
this._terminal.updateRange(buffer.y);
this._dirtyRowService.markDirty(buffer.y);
for (let pos = start; pos < end; ++pos) {
code = data[pos];
@@ -481,7 +471,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._parser.precedingCodepoint = this._workCell.content;
}
}
this._terminal.updateRange(buffer.y);
this._dirtyRowService.markDirty(buffer.y);
}
/**
@@ -514,7 +504,7 @@ export class InputHandler extends Disposable implements IInputHandler {
// make buffer local for faster access
const buffer = this._bufferService.buffer;
if (this._terminal.options.convertEol) {
if (this._optionsService.options.convertEol) {
buffer.x = 0;
}
buffer.y++;
@@ -559,7 +549,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
const originalX = this._bufferService.buffer.x;
this._bufferService.buffer.x = this._bufferService.buffer.nextStop();
if (this._terminal.options.screenReaderMode) {
if (this._optionsService.options.screenReaderMode) {
this._terminal.onA11yTabEmitter.fire(this._bufferService.buffer.x - originalX);
}
}
@@ -830,16 +820,16 @@ export class InputHandler extends Disposable implements IInputHandler {
switch (params.params[0]) {
case 0:
j = this._bufferService.buffer.y;
this._terminal.updateRange(j);
this._dirtyRowService.markDirty(j);
this._eraseInBufferLine(j++, this._bufferService.buffer.x, this._bufferService.cols, this._bufferService.buffer.x === 0);
for (; j < this._bufferService.rows; j++) {
this._resetBufferLine(j);
}
this._terminal.updateRange(j);
this._dirtyRowService.markDirty(j);
break;
case 1:
j = this._bufferService.buffer.y;
this._terminal.updateRange(j);
this._dirtyRowService.markDirty(j);
// Deleted front part of line and everything before. This line will no longer be wrapped.
this._eraseInBufferLine(j, 0, this._bufferService.buffer.x + 1, true);
if (this._bufferService.buffer.x + 1 >= this._bufferService.cols) {
@@ -849,15 +839,15 @@ export class InputHandler extends Disposable implements IInputHandler {
while (j--) {
this._resetBufferLine(j);
}
this._terminal.updateRange(0);
this._dirtyRowService.markDirty(0);
break;
case 2:
j = this._bufferService.rows;
this._terminal.updateRange(j - 1);
this._dirtyRowService.markDirty(j - 1);
while (j--) {
this._resetBufferLine(j);
}
this._terminal.updateRange(0);
this._dirtyRowService.markDirty(0);
break;
case 3:
// Clear scrollback (everything not in viewport)
@@ -897,7 +887,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.cols);
break;
}
this._terminal.updateRange(this._bufferService.buffer.y);
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
}
/**
@@ -926,9 +916,7 @@ export class InputHandler extends Disposable implements IInputHandler {
buffer.lines.splice(row, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
}
// this.maxRange();
this._terminal.updateRange(buffer.y);
this._terminal.updateRange(buffer.scrollBottom);
this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom);
buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
}
@@ -959,9 +947,7 @@ export class InputHandler extends Disposable implements IInputHandler {
buffer.lines.splice(j, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
}
// this.maxRange();
this._terminal.updateRange(buffer.y);
this._terminal.updateRange(buffer.scrollBottom);
this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom);
buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only?
}
@@ -978,7 +964,7 @@ export class InputHandler extends Disposable implements IInputHandler {
params.params[0] || 1,
this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData())
);
this._terminal.updateRange(this._bufferService.buffer.y);
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
}
}
@@ -995,7 +981,7 @@ export class InputHandler extends Disposable implements IInputHandler {
params.params[0] || 1,
this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData())
);
this._terminal.updateRange(this._bufferService.buffer.y);
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
}
}
@@ -1012,9 +998,7 @@ export class InputHandler extends Disposable implements IInputHandler {
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1);
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
}
// this.maxRange();
this._terminal.updateRange(buffer.scrollTop);
this._terminal.updateRange(buffer.scrollBottom);
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
}
/**
@@ -1031,9 +1015,7 @@ export class InputHandler extends Disposable implements IInputHandler {
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
}
// this.maxRange();
this._terminal.updateRange(buffer.scrollTop);
this._terminal.updateRange(buffer.scrollBottom);
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
}
}
@@ -1050,7 +1032,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._bufferService.buffer.x + (params.params[0] || 1),
this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData())
);
this._terminal.updateRange(this._bufferService.buffer.y);
this._dirtyRowService.markDirty(this._bufferService.buffer.y);
}
}
@@ -1893,19 +1875,19 @@ export class InputHandler extends Disposable implements IInputHandler {
switch (param) {
case 1:
case 2:
this._terminal.options.cursorStyle = 'block';
this._optionsService.options.cursorStyle = 'block';
break;
case 3:
case 4:
this._terminal.options.cursorStyle = 'underline';
this._optionsService.options.cursorStyle = 'underline';
break;
case 5:
case 6:
this._terminal.options.cursorStyle = 'bar';
this._optionsService.options.cursorStyle = 'bar';
break;
}
const isBlinking = param % 2 === 1;
this._terminal.options.cursorBlink = isBlinking;
this._optionsService.options.cursorBlink = isBlinking;
}
}
@@ -2097,8 +2079,7 @@ export class InputHandler extends Disposable implements IInputHandler {
const scrollRegionHeight = buffer.scrollBottom - buffer.scrollTop;
buffer.lines.shiftElements(buffer.y + buffer.ybase, scrollRegionHeight, 1);
buffer.lines.set(buffer.y + buffer.ybase, buffer.getBlankLine(this._terminal.eraseAttrData()));
this._terminal.updateRange(buffer.scrollTop);
this._terminal.updateRange(buffer.scrollBottom);
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
} else {
buffer.y--;
this._restrictCursor(); // quickfix to not run out of bounds
@@ -2152,8 +2133,7 @@ export class InputHandler extends Disposable implements IInputHandler {
buffer.lines.get(row).fill(cell);
buffer.lines.get(row).isWrapped = false;
}
this._terminal.updateRange(0);
this._terminal.updateRange(this._bufferService.rows);
this._dirtyRowService.markAllDirty();
this._setCursor(0, 0);
}
}
+40 -75
View File
@@ -30,7 +30,7 @@ import { C0 } from 'common/data/EscapeSequences';
import { InputHandler } from './InputHandler';
import { Renderer } from './renderer/Renderer';
import { Linkifier } from 'browser/Linkifier';
import { SelectionService } from './browser/services/SelectionService';
import { SelectionService } from 'browser/services/SelectionService';
import * as Browser from 'common/Platform';
import { addDisposableDomListener } from 'browser/Lifecycle';
import * as Strings from 'browser/LocalizableStrings';
@@ -47,7 +47,7 @@ import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { applyWindowsMode } from './WindowsMode';
import { ColorManager } from 'browser/ColorManager';
import { RenderService } from 'browser/services/RenderService';
import { IOptionsService, IBufferService, ICoreService, ILogService } from 'common/services/Services';
import { IOptionsService, IBufferService, ICoreService, ILogService, IDirtyRowService, IInstantiationService } from 'common/services/Services';
import { OptionsService } from 'common/services/OptionsService';
import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService } from 'browser/services/Services';
import { CharSizeService } from 'browser/services/CharSizeService';
@@ -60,6 +60,8 @@ import { IParams } from 'common/parser/Types';
import { CoreService } from 'common/services/CoreService';
import { LogService } from 'common/services/LogService';
import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport } from 'browser/Types';
import { DirtyRowService } from 'common/services/DirtyRowService';
import { InstantiationService } from 'common/services/InstantiationService';
// Let it work inside Node.js for automated testing purposes.
const document = (typeof window !== 'undefined') ? window.document : null;
@@ -111,6 +113,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// common services
private _bufferService: IBufferService;
private _coreService: ICoreService;
private _dirtyRowService: IDirtyRowService;
private _instantiationService: IInstantiationService;
private _logService: ILogService;
public optionsService: IOptionsService;
@@ -148,8 +152,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
public urxvtMouse: boolean;
// misc
private _refreshStart: number;
private _refreshEnd: number;
public savedCols: number;
public curAttrData: IAttributeData;
@@ -239,11 +241,18 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
super();
// Setup and initialize common services
this._instantiationService = new InstantiationService();
this.optionsService = new OptionsService(options);
this._bufferService = new BufferService(this.optionsService);
this._coreService = new CoreService(() => this.scrollToBottom(), this._bufferService, this.optionsService);
this._instantiationService.setService(IOptionsService, this.optionsService);
this._bufferService = this._instantiationService.createInstance(BufferService);
this._instantiationService.setService(IBufferService, this._bufferService);
this._coreService = this._instantiationService.createInstance(CoreService, () => this.scrollToBottom());
this._instantiationService.setService(ICoreService, this._coreService);
this._coreService.onData(e => this._onData.fire(e));
this._logService = new LogService(this.optionsService);
this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService);
this._instantiationService.setService(IDirtyRowService, this._dirtyRowService);
this._logService = this._instantiationService.createInstance(LogService);
this._instantiationService.setService(ILogService, this._logService);
this._setupOptionsListeners();
this._setup();
@@ -300,7 +309,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._userScrolling = false;
// Register input handler and refire/handle events
this._inputHandler = new InputHandler(this, this._bufferService, this._coreService, this._logService, this.optionsService);
this._inputHandler = new InputHandler(this, this._bufferService, this._coreService, this._dirtyRowService, this._logService, this.optionsService);
this._inputHandler.onCursorMove(() => this._onCursorMove.fire());
this._inputHandler.onLineFeed(() => this._onLineFeed.fire());
this.register(this._inputHandler);
@@ -385,7 +394,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
case 'screenReaderMode':
if (this.optionsService.options.screenReaderMode) {
if (!this._accessibilityManager && this._renderService) {
this._accessibilityManager = new AccessibilityManager(this, this._renderService.dimensions);
this._accessibilityManager = new AccessibilityManager(this, this._renderService);
}
} else {
if (this._accessibilityManager) {
@@ -576,11 +585,12 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this.register(addDisposableDomListener(this.textarea, 'blur', () => this._onTextAreaBlur()));
this._helperContainer.appendChild(this.textarea);
this._charSizeService = new CharSizeService(this._document, this._helperContainer, this.optionsService);
this._charSizeService = this._instantiationService.createInstance(CharSizeService, this._document, this._helperContainer);
this._instantiationService.setService(ICharSizeService, this._charSizeService);
this._compositionView = document.createElement('div');
this._compositionView.classList.add('composition-view');
this._compositionHelper = new CompositionHelper(this.textarea, this._compositionView, this._bufferService, this.optionsService, this._charSizeService, this._coreService);
this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView);
this._helperContainer.appendChild(this._compositionView);
// Performance: Add viewport and helper elements from the fragment
@@ -592,20 +602,20 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._colorManager.setTheme(this._theme);
const renderer = this._createRenderer();
this._renderService = new RenderService(renderer, this.rows, this.screenElement, this.optionsService, this._charSizeService);
this._renderService = this._instantiationService.createInstance(RenderService, renderer, this.rows, this.screenElement);
this._instantiationService.setService(IRenderService, this._renderService);
this._renderService.onRender(e => this._onRender.fire(e));
this.onResize(e => this._renderService.resize(e.cols, e.rows));
this._soundService = new SoundService(this.optionsService);
this._mouseService = new MouseService(this._renderService, this._charSizeService);
this._soundService = this._instantiationService.createInstance(SoundService);
this._instantiationService.setService(ISoundService, this._soundService);
this._mouseService = this._instantiationService.createInstance(MouseService);
this._instantiationService.setService(IMouseService, this._mouseService);
this.viewport = new Viewport(
this.viewport = this._instantiationService.createInstance(Viewport,
(amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent),
this._viewportElement,
this._viewportScrollArea,
this._bufferService,
this._charSizeService,
this._renderService
this._viewportScrollArea
);
this.viewport.onThemeChange(this._colorManager.colors);
this.register(this.viewport);
@@ -616,11 +626,11 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this.register(this.onFocus(() => this._renderService.onFocus()));
this.register(this._renderService.onDimensionsChange(() => this.viewport.syncScrollArea()));
this._selectionService = new SelectionService(
this._selectionService = this._instantiationService.createInstance(SelectionService,
(amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent),
this.element, this.screenElement, this._charSizeService, this._bufferService, this._coreService,
this._mouseService, this.optionsService
);
this.element,
this.screenElement);
this._instantiationService.setService(ISelectionService, this._selectionService);
this.register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire()));
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService.onMouseDown(e)));
this.register(this._selectionService.onRedrawRequest(e => this._renderService.onSelectionChanged(e.start, e.end, e.columnSelectMode)));
@@ -638,7 +648,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}));
this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this._selectionService.refresh()));
this._mouseZoneManager = new MouseZoneManager(this.element, this.screenElement, this._bufferService, this._mouseService, this._selectionService);
this._mouseZoneManager = this._instantiationService.createInstance(MouseZoneManager, this.element, this.screenElement);
this.register(this._mouseZoneManager);
this.register(this.onScroll(() => this._mouseZoneManager.clearAll()));
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
@@ -655,8 +665,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
if (this.options.screenReaderMode) {
// Note that this must be done *after* the renderer is created in order to
// ensure the correct order of the dprchange event
this._accessibilityManager = new AccessibilityManager(this, this._renderService.dimensions);
this._accessibilityManager.register(this._renderService.onDimensionsChange(e => this._accessibilityManager.setDimensions(e)));
this._accessibilityManager = new AccessibilityManager(this, this._renderService);
}
// Measure the character size
@@ -676,8 +685,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
private _createRenderer(): IRenderer {
switch (this.options.rendererType) {
case 'canvas': return new Renderer(this, this._colorManager.colors, this._charSizeService); break;
case 'dom': return new DomRenderer(this, this._colorManager.colors, this._charSizeService, this.optionsService); break;
case 'canvas': return new Renderer(this._colorManager.colors, this, this._bufferService, this._charSizeService);
case 'dom': return new DomRenderer(this, this._colorManager.colors, this._charSizeService, this.optionsService);
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
}
}
@@ -1136,8 +1145,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
// Flag rows that need updating
this.updateRange(this.buffer.scrollTop);
this.updateRange(this.buffer.scrollBottom);
this._dirtyRowService.markRangeDirty(this.buffer.scrollTop, this.buffer.scrollBottom);
this._onScroll.fire(this.buffer.ydisp);
}
@@ -1258,19 +1266,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._xoffSentToCatchUp = false;
}
this._refreshStart = this.buffer.y;
this._refreshEnd = this.buffer.y;
// HACK: Set the parser state based on it's state at the time of return.
// This works around the bug #662 which saw the parser state reset in the
// middle of parsing escape sequence in two chunks. For some reason the
// state of the parser resets to 0 after exiting parser.parse. This change
// just sets the state back based on the correct return statement.
this._inputHandler.parseUtf8(data);
this.updateRange(this.buffer.y);
this.refresh(this._refreshStart, this._refreshEnd);
this.refresh(this._dirtyRowService.start, this._dirtyRowService.end);
if (Date.now() - startTime >= WRITE_TIMEOUT_MS) {
break;
@@ -1345,19 +1343,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._xoffSentToCatchUp = false;
}
this._refreshStart = this.buffer.y;
this._refreshEnd = this.buffer.y;
// HACK: Set the parser state based on it's state at the time of return.
// This works around the bug #662 which saw the parser state reset in the
// middle of parsing escape sequence in two chunks. For some reason the
// state of the parser resets to 0 after exiting parser.parse. This change
// just sets the state back based on the correct return statement.
this._inputHandler.parse(data);
this.updateRange(this.buffer.y);
this.refresh(this._refreshStart, this._refreshEnd);
this.refresh(this._dirtyRowService.start, this._dirtyRowService.end);
if (Date.now() - startTime >= WRITE_TIMEOUT_MS) {
break;
@@ -1717,29 +1705,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._onResize.fire({ cols: x, rows: y });
}
/**
* Updates the range of rows to refresh
* @param y The number of rows to refresh next.
*/
public updateRange(y: number): void {
if (y < this._refreshStart) this._refreshStart = y;
if (y > this._refreshEnd) this._refreshEnd = y;
// if (y > this.refreshEnd) {
// this.refreshEnd = y;
// if (y > this.rows - 1) {
// this.refreshEnd = this.rows - 1;
// }
// }
}
/**
* Set the range of refreshing to the maximum value
*/
public maxRange(): void {
this._refreshStart = 0;
this._refreshEnd = this.rows - 1;
}
/**
* Clear the entire buffer, making the prompt line the new first line.
*/
-1
View File
@@ -55,7 +55,6 @@ export interface IInputHandlingTerminal {
bell(): void;
focus(): void;
updateRange(y: number): void;
scroll(isWrapped?: boolean): void;
setgLevel(g: number): void;
eraseAttrData(): IAttributeData;
+3 -3
View File
@@ -35,9 +35,9 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager {
constructor(
private readonly _element: HTMLElement,
private readonly _screenElement: HTMLElement,
private readonly _bufferService: IBufferService,
private readonly _mouseService: IMouseService,
private readonly _selectionService: ISelectionService
@IBufferService private readonly _bufferService: IBufferService,
@IMouseService private readonly _mouseService: IMouseService,
@ISelectionService private readonly _selectionService: ISelectionService
) {
super();
+2
View File
@@ -7,6 +7,7 @@ import { IEvent, EventEmitter } from 'common/EventEmitter';
import { ICharSizeService, IMouseService } from 'browser/services/Services';
export class MockCharSizeService implements ICharSizeService {
serviceBrand: any;
get hasValidSize(): boolean { return this.width > 0 && this.height > 0; }
onCharSizeChange: IEvent<void> = new EventEmitter<void>().event;
constructor(public width: number, public height: number) {}
@@ -14,6 +15,7 @@ export class MockCharSizeService implements ICharSizeService {
}
export class MockMouseService implements IMouseService {
serviceBrand: any;
public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined {
throw new Error('Not implemented');
}
+3 -3
View File
@@ -36,9 +36,9 @@ export class Viewport extends Disposable implements IViewport {
private readonly _scrollLines: (amount: number, suppressEvent: boolean) => void,
private readonly _viewportElement: HTMLElement,
private readonly _scrollArea: HTMLElement,
private readonly _bufferService: IBufferService,
private readonly _charSizeService: ICharSizeService,
private readonly _renderService: IRenderService
@IBufferService private readonly _bufferService: IBufferService,
@ICharSizeService private readonly _charSizeService: ICharSizeService,
@IRenderService private readonly _renderService: IRenderService
) {
super();
+4 -4
View File
@@ -37,10 +37,10 @@ export class CompositionHelper {
constructor(
private readonly _textarea: HTMLTextAreaElement,
private readonly _compositionView: HTMLElement,
private readonly _bufferService: IBufferService,
private readonly _optionsService: IOptionsService,
private readonly _charSizeService: ICharSizeService,
private readonly _coreService: ICoreService
@IBufferService private readonly _bufferService: IBufferService,
@IOptionsService private readonly _optionsService: IOptionsService,
@ICharSizeService private readonly _charSizeService: ICharSizeService,
@ICoreService private readonly _coreService: ICoreService
) {
this._isComposing = false;
this._isSendingComposition = false;
+5 -3
View File
@@ -8,6 +8,8 @@ import { IEvent, EventEmitter } from 'common/EventEmitter';
import { ICharSizeService } from 'browser/services/Services';
export class CharSizeService implements ICharSizeService {
serviceBrand: any;
public width: number = 0;
public height: number = 0;
private _measureStrategy: IMeasureStrategy;
@@ -18,9 +20,9 @@ export class CharSizeService implements ICharSizeService {
public get onCharSizeChange(): IEvent<void> { return this._onCharSizeChange.event; }
constructor(
document: Document,
parentElement: HTMLElement,
private _optionsService: IOptionsService
readonly document: Document,
readonly parentElement: HTMLElement,
@IOptionsService private readonly _optionsService: IOptionsService
) {
this._measureStrategy = new DomMeasureStrategy(document, parentElement, this._optionsService);
}
+4 -2
View File
@@ -7,9 +7,11 @@ import { ICharSizeService, IRenderService, IMouseService } from './Services';
import { getCoords, getRawByteCoords } from 'browser/input/Mouse';
export class MouseService implements IMouseService {
serviceBrand: any;
constructor(
private readonly _renderService: IRenderService,
private readonly _charSizeService: ICharSizeService
@IRenderService private readonly _renderService: IRenderService,
@ICharSizeService private readonly _charSizeService: ICharSizeService
) {
}
+5 -3
View File
@@ -14,6 +14,8 @@ import { IOptionsService } from 'common/services/Services';
import { ICharSizeService, IRenderService } from 'browser/services/Services';
export class RenderService extends Disposable implements IRenderService {
serviceBrand: any;
private _renderDebouncer: RenderDebouncer;
private _screenDprMonitor: ScreenDprMonitor;
@@ -34,9 +36,9 @@ export class RenderService extends Disposable implements IRenderService {
constructor(
private _renderer: IRenderer,
private _rowCount: number,
screenElement: HTMLElement,
optionsService: IOptionsService,
charSizeService: ICharSizeService
readonly screenElement: HTMLElement,
@IOptionsService readonly optionsService: IOptionsService,
@ICharSizeService readonly charSizeService: ICharSizeService
) {
super();
this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end));
+7 -5
View File
@@ -67,6 +67,8 @@ export const enum SelectionMode {
* when the selection is ready to be redrawn (on an animation frame).
*/
export class SelectionService implements ISelectionService {
serviceBrand: any;
protected _model: SelectionModel;
/**
@@ -114,11 +116,11 @@ export class SelectionService implements ISelectionService {
private readonly _scrollLines: (amount: number, suppressEvent: boolean) => void,
private readonly _element: HTMLElement,
private readonly _screenElement: HTMLElement,
private readonly _charSizeService: ICharSizeService,
private readonly _bufferService: IBufferService,
private readonly _coreService: ICoreService,
private readonly _mouseService: IMouseService,
private readonly _optionsService: IOptionsService
@ICharSizeService private readonly _charSizeService: ICharSizeService,
@IBufferService private readonly _bufferService: IBufferService,
@ICoreService private readonly _coreService: ICoreService,
@IMouseService private readonly _mouseService: IMouseService,
@IOptionsService private readonly _optionsService: IOptionsService
) {
// Init listeners
this._mouseMoveListener = event => this._onMouseMove(<MouseEvent>event);
@@ -7,8 +7,12 @@ import { IEvent } from 'common/EventEmitter';
import { IRenderDimensions, IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types';
import { IColorSet } from 'browser/Types';
import { ISelectionRedrawRequestEvent } from 'browser/selection/Types';
import { createDecorator } from 'common/services/ServiceRegistry';
export const ICharSizeService = createDecorator<ICharSizeService>('CharSizeService');
export interface ICharSizeService {
serviceBrand: any;
readonly width: number;
readonly height: number;
readonly hasValidSize: boolean;
@@ -18,12 +22,18 @@ export interface ICharSizeService {
measure(): void;
}
export const IMouseService = createDecorator<IMouseService>('MouseService');
export interface IMouseService {
serviceBrand: any;
getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined;
getRawByteCoords(event: MouseEvent, element: HTMLElement, colCount: number, rowCount: number): { x: number, y: number } | undefined;
}
export const IRenderService = createDecorator<IRenderService>('RenderService');
export interface IRenderService {
serviceBrand: any;
onDimensionsChange: IEvent<IRenderDimensions>;
onRender: IEvent<{ start: number, end: number }>;
onRefreshRequest: IEvent<{ start: number, end: number }>;
@@ -48,7 +58,10 @@ export interface IRenderService {
deregisterCharacterJoiner(joinerId: number): boolean;
}
export const ISelectionService = createDecorator<ISelectionService>('SelectionService');
export interface ISelectionService {
serviceBrand: any;
readonly selectionText: string;
readonly hasSelection: boolean;
readonly selectionStart: [number, number] | undefined;
@@ -73,6 +86,9 @@ export interface ISelectionService {
onMouseDown(event: MouseEvent): void;
}
export const ISoundService = createDecorator<ISoundService>('SoundService');
export interface ISoundService {
serviceBrand: any;
playBellSound(): void;
}
+3 -1
View File
@@ -7,6 +7,8 @@ import { IOptionsService } from 'common/services/Services';
import { ISoundService } from 'browser/services/Services';
export class SoundService implements ISoundService {
serviceBrand: any;
private static _audioContext: AudioContext;
static get audioContext(): AudioContext | null {
@@ -22,7 +24,7 @@ export class SoundService implements ISoundService {
}
constructor(
private _optionsService: IOptionsService
@IOptionsService private _optionsService: IOptionsService
) {
}
+15 -1
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IPartialTerminalOptions } from 'common/services/Services';
import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IPartialTerminalOptions, IDirtyRowService } from 'common/services/Services';
import { IEvent, EventEmitter } from 'common/EventEmitter';
import { clone } from 'common/Clone';
import { DEFAULT_OPTIONS } from 'common/services/OptionsService';
@@ -12,6 +12,7 @@ import { BufferSet } from 'common/buffer/BufferSet';
import { IDecPrivateModes } from 'common/Types';
export class MockBufferService implements IBufferService {
serviceBrand: any;
public get buffer(): IBuffer { return this.buffers.active; }
public buffers: IBufferSet = {} as any;
constructor(
@@ -29,6 +30,7 @@ export class MockBufferService implements IBufferService {
}
export class MockCoreService implements ICoreService {
serviceBrand: any;
decPrivateModes: IDecPrivateModes = {} as any;
onData: IEvent<string> = new EventEmitter<string>().event;
onUserInput: IEvent<void> = new EventEmitter<void>().event;
@@ -36,7 +38,18 @@ export class MockCoreService implements ICoreService {
triggerDataEvent(data: string, wasUserInput?: boolean): void {}
}
export class MockDirtyRowService implements IDirtyRowService {
serviceBrand: any;
start: number = 0;
end: number = 0;
clearRange(): void {}
markDirty(y: number): void {}
markRangeDirty(y1: number, y2: number): void {}
markAllDirty(): void {}
}
export class MockLogService implements ILogService {
serviceBrand: any;
debug(message: any, ...optionalParams: any[]): void {}
info(message: any, ...optionalParams: any[]): void {}
warn(message: any, ...optionalParams: any[]): void {}
@@ -44,6 +57,7 @@ export class MockLogService implements ILogService {
}
export class MockOptionsService implements IOptionsService {
serviceBrand: any;
options: ITerminalOptions = clone(DEFAULT_OPTIONS);
onOptionChange: IEvent<string> = new EventEmitter<string>().event;
constructor(testOptions?: IPartialTerminalOptions) {
+5
View File
@@ -153,3 +153,8 @@ export interface IMarker extends IDisposable {
export interface IDecPrivateModes {
applicationCursorKeys: boolean;
}
export interface IRowRange {
start: number;
end: number;
}
+3 -1
View File
@@ -11,6 +11,8 @@ export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars
export const MINIMUM_ROWS = 1;
export class BufferService implements IBufferService {
serviceBrand: any;
public cols: number;
public rows: number;
public buffers: IBufferSet;
@@ -18,7 +20,7 @@ export class BufferService implements IBufferService {
public get buffer(): IBuffer { return this.buffers.active; }
constructor(
private _optionsService: IOptionsService
@IOptionsService private _optionsService: IOptionsService
) {
this.cols = Math.max(_optionsService.options.cols, MINIMUM_COLS);
this.rows = Math.max(_optionsService.options.rows, MINIMUM_ROWS);
+4 -2
View File
@@ -13,6 +13,8 @@ const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({
});
export class CoreService implements ICoreService {
serviceBrand: any;
public decPrivateModes: IDecPrivateModes;
private _onData = new EventEmitter<string>();
@@ -23,8 +25,8 @@ export class CoreService implements ICoreService {
constructor(
// TODO: Move this into a service
private readonly _scrollToBottom: () => void,
private readonly _bufferService: IBufferService,
private readonly _optionsService: IOptionsService
@IBufferService private readonly _bufferService: IBufferService,
@IOptionsService private readonly _optionsService: IOptionsService
) {
this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES);
}

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