Merge branch 'master' into left_right_scroll

This commit is contained in:
jerch
2019-10-04 20:12:34 +02:00
committed by GitHub
29 changed files with 437 additions and 407 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ npm install --save xterm-addon-attach
```ts
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-attach';
import { AttachAddon } from 'xterm-addon-attach';
const terminal = new Terminal();
const attachAddon = new AttachAddon(webSocket);
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-attach",
"version": "0.2.0",
"version": "0.2.1",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
@@ -16,6 +16,6 @@
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^3.14.0"
"xterm": "^4.0.0"
}
}
@@ -54,7 +54,7 @@ describe('AttachAddon', () => {
const server = new WebSocket.Server({ port });
const data = new Uint8Array([102, 111, 111]);
server.on('connection', socket => socket.send(data));
await page.evaluate(`window.term.loadAddon(new window.AttachAddon(new WebSocket('ws://localhost:${port}'), { inputUtf8: true }))`);
await page.evaluate(`window.term.loadAddon(new window.AttachAddon(new WebSocket('ws://localhost:${port}')))`);
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo');
server.close();
});
+7 -11
View File
@@ -9,13 +9,11 @@ import { Terminal, IDisposable, ITerminalAddon } from 'xterm';
interface IAttachOptions {
bidirectional?: boolean;
inputUtf8?: boolean;
}
export class AttachAddon implements ITerminalAddon {
private _socket: WebSocket;
private _bidirectional: boolean;
private _utf8: boolean;
private _disposables: IDisposable[] = [];
constructor(socket: WebSocket, options?: IAttachOptions) {
@@ -23,17 +21,15 @@ export class AttachAddon implements ITerminalAddon {
// always set binary type to arraybuffer, we do not handle blobs
this._socket.binaryType = 'arraybuffer';
this._bidirectional = (options && options.bidirectional === false) ? false : true;
this._utf8 = !!(options && options.inputUtf8);
}
public activate(terminal: Terminal): void {
if (this._utf8) {
this._disposables.push(addSocketListener(this._socket, 'message',
(ev: MessageEvent | Event | CloseEvent) => terminal.writeUtf8(new Uint8Array((ev as any).data as ArrayBuffer))));
} else {
this._disposables.push(addSocketListener(this._socket, 'message',
(ev: MessageEvent | Event | CloseEvent) => terminal.write((ev as any).data as string)));
}
this._disposables.push(
addSocketListener(this._socket, 'message', ev => {
const data: ArrayBuffer | string = ev.data;
terminal.write(typeof data === 'string' ? data : new Uint8Array(data));
})
);
if (this._bidirectional) {
this._disposables.push(terminal.onData(data => this._sendData(data)));
@@ -57,7 +53,7 @@ export class AttachAddon implements ITerminalAddon {
}
}
function addSocketListener(socket: WebSocket, type: string, handler: (this: WebSocket, ev: MessageEvent | Event | CloseEvent) => any): IDisposable {
function addSocketListener<K extends keyof WebSocketEventMap>(socket: WebSocket, type: K, handler: (this: WebSocket, ev: WebSocketEventMap[K]) => any): IDisposable {
socket.addEventListener(type, handler);
return {
dispose: () => {
@@ -11,14 +11,6 @@ declare module 'xterm-addon-attach' {
* Whether input should be written to the backend. Defaults to `true`.
*/
bidirectional?: boolean;
/**
* Whether to use UTF8 binary transport for incoming messages. Defaults to `false`.
* Note: This must be in line with the server side of the websocket.
* Always send string messages from the backend if this options is false,
* otherwise always binary UTF8 data.
*/
inputUtf8?: boolean;
}
export class AttachAddon implements ITerminalAddon {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-fit",
"version": "0.2.0",
"version": "0.2.1",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
@@ -16,6 +16,6 @@
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^3.14.0"
"xterm": "^4.0.0"
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-search",
"version": "0.2.0",
"version": "0.2.1",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
@@ -16,6 +16,6 @@
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^3.14.0"
"xterm": "^4.0.0"
}
}
@@ -111,12 +111,7 @@ async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
}
async function writeSync(data: string): Promise<void> {
await page.evaluate(`window.term.write('${data}');`);
while (true) {
if (await page.evaluate(`window.term._core.writeBuffer.length === 0`)) {
break;
}
}
return page.evaluate(`new Promise(resolve => window.term.write('${data}', resolve))`);
}
function makeData(length: number): string {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-web-links",
"version": "0.2.0",
"version": "0.2.1",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
@@ -16,6 +16,6 @@
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^3.14.0"
"xterm": "^4.0.0"
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-webgl",
"version": "0.2.0",
"version": "0.2.1",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
@@ -16,6 +16,6 @@
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^3.14.0"
"xterm": "^4.0.0"
}
}
@@ -145,12 +145,7 @@ async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
}
async function writeSync(data: string): Promise<void> {
await page.evaluate(`window.term.write('${data}');`);
while (true) {
if (await page.evaluate(`window.term._core.writeBuffer.length === 0`)) {
break;
}
}
return page.evaluate(`new Promise(resolve => window.term.write('${data}', resolve))`);
}
async function getCellColor(col: number, row: number): Promise<number[]> {
+1 -2
View File
@@ -121,8 +121,7 @@ jobs:
displayName: 'Install Yarn'
- script: |
yarn
BUILD_DIR=dist npm run build
displayName: 'Install dependencies and build'
- script: |
NPM_AUTH_TOKEN="$(NPM_AUTH_TOKEN)" node ./bin/publish.js
displayName: 'Publish to npm'
displayName: 'Package and publish to npm'
-7
View File
@@ -167,14 +167,7 @@ function createTerminal(): void {
}
function runRealTerminal(): void {
/**
* The demo defaults to string transport by default.
* To run it with UTF8 binary transport, swap comment on
* the lines below. (Must also be switched in server.js)
*/
term.loadAddon(new AttachAddon(socket));
// term.loadAddon(new AttachAddon(socket, {inputUtf8: true}));
term._initialized = true;
}
+4 -8
View File
@@ -3,12 +3,8 @@ var expressWs = require('express-ws');
var os = require('os');
var pty = require('node-pty');
/**
* Whether to use UTF8 binary transport.
* (Must also be switched in client.ts)
*/
const USE_BINARY_UTF8 = false;
// Whether to use binary transport.
const USE_BINARY = os.platform() !== "win32";
function startServer() {
var app = express();
@@ -46,7 +42,7 @@ function startServer() {
rows: rows || 24,
cwd: env.PWD,
env: env,
encoding: USE_BINARY_UTF8 ? null : 'utf8'
encoding: USE_BINARY ? null : 'utf8'
});
console.log('Created terminal with PID: ' + term.pid);
@@ -108,7 +104,7 @@ function startServer() {
}
};
}
const send = USE_BINARY_UTF8 ? bufferUtf8(ws, 5) : buffer(ws, 5);
const send = USE_BINARY ? bufferUtf8(ws, 5) : buffer(ws, 5);
term.on('data', function(data) {
try {
+7 -20
View File
@@ -328,7 +328,7 @@ export class InputHandler extends Disposable implements IInputHandler {
super.dispose();
}
public parse(data: string): void {
public parse(data: string | Uint8Array): void {
let buffer = this._bufferService.buffer;
const cursorStartX = buffer.x;
const cursorStartY = buffer.y;
@@ -338,30 +338,17 @@ export class InputHandler extends Disposable implements IInputHandler {
if (this._parseBuffer.length < data.length) {
this._parseBuffer = new Uint32Array(data.length);
}
this._parser.parse(this._parseBuffer, this._stringDecoder.decode(data, this._parseBuffer));
buffer = this._bufferService.buffer;
if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
this._onCursorMove.fire();
}
}
public parseUtf8(data: Uint8Array): void {
let buffer = this._bufferService.buffer;
const cursorStartX = buffer.x;
const cursorStartY = buffer.y;
this._logService.debug('parsing data', data);
if (this._parseBuffer.length < data.length) {
this._parseBuffer = new Uint32Array(data.length);
}
this._parser.parse(this._parseBuffer, this._utf8Decoder.decode(data, this._parseBuffer));
this._parser.parse(this._parseBuffer,
(typeof data === 'string')
? this._stringDecoder.decode(data, this._parseBuffer)
: this._utf8Decoder.decode(data, this._parseBuffer)
);
buffer = this._bufferService.buffer;
if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
this._onCursorMove.fire();
}
this._terminal.refresh(this._dirtyRowService.start, this._dirtyRowService.end);
}
public print(data: Uint32Array, start: number, end: number): void {
+37 -42
View File
@@ -29,11 +29,6 @@ describe('Terminal', () => {
(<any>term).renderer = new MockRenderer();
term.viewport = new MockViewport();
(<any>term)._compositionHelper = new MockCompositionHelper();
// Force synchronous writes
term.write = (data) => {
term.writeBuffer.push(data);
(<any>term)._innerWrite();
};
(<any>term).element = {
classList: {
toggle: () => { },
@@ -59,18 +54,18 @@ describe('Terminal', () => {
// });
it('should fire the onCursorMove event', (done) => {
term.onCursorMove(() => done());
term.write('foo');
term.writeSync('foo');
});
it('should fire the onLineFeed event', (done) => {
term.onLineFeed(() => done());
term.write('\n');
term.writeSync('\n');
});
it('should fire a scroll event when scrollback is created', (done) => {
term.onScroll(() => done());
term.write('\n'.repeat(INIT_ROWS));
term.writeSync('\n'.repeat(INIT_ROWS));
});
it('should fire a scroll event when scrollback is cleared', (done) => {
term.write('\n'.repeat(INIT_ROWS));
term.writeSync('\n'.repeat(INIT_ROWS));
term.onScroll(() => done());
term.clear();
});
@@ -195,7 +190,7 @@ describe('Terminal', () => {
it('should clear a buffer larger than rows', () => {
// Fill the buffer with dummy rows
for (let i = 0; i < term.rows * 2; i++) {
term.write('test\n');
term.writeSync('test\n');
}
const promptLine = term.buffer.lines.get(term.buffer.ybase + term.buffer.y);
@@ -244,7 +239,7 @@ describe('Terminal', () => {
assert.equal(e, '\x1b[200~foo\x1b[201~');
done();
});
term.write('\x1b[?2004h');
term.writeSync('\x1b[?2004h');
term.paste('foo');
});
});
@@ -254,7 +249,7 @@ describe('Terminal', () => {
let startYDisp: number;
beforeEach(() => {
for (let i = 0; i < INIT_ROWS * 2; i++) {
term.writeln('test');
term.writeSync('test\r\n');
}
startYDisp = INIT_ROWS + 1;
});
@@ -289,7 +284,7 @@ describe('Terminal', () => {
let startYDisp: number;
beforeEach(() => {
for (let i = 0; i < term.rows * 3; i++) {
term.writeln('test');
term.writeSync('test\r\n');
}
startYDisp = (term.rows * 2) + 1;
});
@@ -312,7 +307,7 @@ describe('Terminal', () => {
describe('scrollToTop', () => {
beforeEach(() => {
for (let i = 0; i < term.rows * 3; i++) {
term.writeln('test');
term.writeSync('test\r\n');
}
});
it('should scroll to the top', () => {
@@ -326,7 +321,7 @@ describe('Terminal', () => {
let startYDisp: number;
beforeEach(() => {
for (let i = 0; i < term.rows * 3; i++) {
term.writeln('test');
term.writeSync('test\r\n');
}
startYDisp = (term.rows * 2) + 1;
});
@@ -347,7 +342,7 @@ describe('Terminal', () => {
let startYDisp: number;
beforeEach(() => {
for (let i = 0; i < term.rows * 3; i++) {
term.writeln('test');
term.writeSync('test\r\n');
}
startYDisp = (term.rows * 2) + 1;
});
@@ -392,7 +387,7 @@ describe('Terminal', () => {
it('should not scroll down, when a custom keydown handler prevents the event', () => {
// Add some output to the terminal
for (let i = 0; i < term.rows * 3; i++) {
term.writeln('test');
term.writeSync('test\r\n');
}
const startYDisp = (term.rows * 2) + 1;
term.attachCustomKeyEventHandler(() => {
@@ -737,7 +732,7 @@ describe('Terminal', () => {
const high = String.fromCharCode(0xD800);
const cell = new CellData();
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
term.write(high + String.fromCharCode(i));
term.writeSync(high + String.fromCharCode(i));
const tchar = term.buffer.lines.get(0).loadCell(0, cell);
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
expect(tchar.getChars().length).eql(2);
@@ -751,7 +746,7 @@ describe('Terminal', () => {
const cell = new CellData();
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
term.buffer.x = term.cols - 1;
term.write(high + String.fromCharCode(i));
term.writeSync(high + String.fromCharCode(i));
expect(term.buffer.lines.get(0).loadCell(term.buffer.x - 1, cell).getChars()).eql(high + String.fromCharCode(i));
expect(term.buffer.lines.get(0).loadCell(term.buffer.x - 1, cell).getChars().length).eql(2);
expect(term.buffer.lines.get(1).loadCell(0, cell).getChars()).eql('');
@@ -764,7 +759,7 @@ describe('Terminal', () => {
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
term.buffer.x = term.cols - 1;
term.wraparoundMode = true;
term.write('a' + high + String.fromCharCode(i));
term.writeSync('a' + high + String.fromCharCode(i));
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars()).eql('a');
expect(term.buffer.lines.get(1).loadCell(0, cell).getChars()).eql(high + String.fromCharCode(i));
expect(term.buffer.lines.get(1).loadCell(0, cell).getChars().length).eql(2);
@@ -782,7 +777,7 @@ describe('Terminal', () => {
if (width !== 1) {
continue;
}
term.write('a' + high + String.fromCharCode(i));
term.writeSync('a' + high + String.fromCharCode(i));
// auto wraparound mode should cut off the rest of the line
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars()).eql(high + String.fromCharCode(i));
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars().length).eql(2);
@@ -794,8 +789,8 @@ describe('Terminal', () => {
const high = String.fromCharCode(0xD800);
const cell = new CellData();
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
term.write(high);
term.write(String.fromCharCode(i));
term.writeSync(high);
term.writeSync(String.fromCharCode(i));
const tchar = term.buffer.lines.get(0).loadCell(0, cell);
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
expect(tchar.getChars().length).eql(2);
@@ -809,7 +804,7 @@ describe('Terminal', () => {
describe('unicode - combining characters', () => {
const cell = new CellData();
it('café', () => {
term.write('cafe\u0301');
term.writeSync('cafe\u0301');
term.buffer.lines.get(0).loadCell(3, cell);
expect(cell.getChars()).eql('e\u0301');
expect(cell.getChars().length).eql(2);
@@ -817,7 +812,7 @@ describe('Terminal', () => {
});
it('café - end of line', () => {
term.buffer.x = term.cols - 1 - 3;
term.write('cafe\u0301');
term.writeSync('cafe\u0301');
term.buffer.lines.get(0).loadCell(term.cols - 1, cell);
expect(cell.getChars()).eql('e\u0301');
expect(cell.getChars().length).eql(2);
@@ -829,7 +824,7 @@ describe('Terminal', () => {
});
it('multiple combined é', () => {
term.wraparoundMode = true;
term.write(Array(100).join('e\u0301'));
term.writeSync(Array(100).join('e\u0301'));
for (let i = 0; i < term.cols; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
expect(cell.getChars()).eql('e\u0301');
@@ -843,7 +838,7 @@ describe('Terminal', () => {
});
it('multiple surrogate with combined', () => {
term.wraparoundMode = true;
term.write(Array(100).join('\uD800\uDC00\u0301'));
term.writeSync(Array(100).join('\uD800\uDC00\u0301'));
for (let i = 0; i < term.cols; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
expect(cell.getChars()).eql('\uD800\uDC00\u0301');
@@ -861,18 +856,18 @@ describe('Terminal', () => {
const cell = new CellData();
it('cursor movement even', () => {
expect(term.buffer.x).eql(0);
term.write('¥');
term.writeSync('¥');
expect(term.buffer.x).eql(2);
});
it('cursor movement odd', () => {
term.buffer.x = 1;
expect(term.buffer.x).eql(1);
term.write('¥');
term.writeSync('¥');
expect(term.buffer.x).eql(3);
});
it('line of ¥ even', () => {
term.wraparoundMode = true;
term.write(Array(50).join('¥'));
term.writeSync(Array(50).join('¥'));
for (let i = 0; i < term.cols; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
if (i % 2) {
@@ -893,7 +888,7 @@ describe('Terminal', () => {
it('line of ¥ odd', () => {
term.wraparoundMode = true;
term.buffer.x = 1;
term.write(Array(50).join('¥'));
term.writeSync(Array(50).join('¥'));
for (let i = 1; i < term.cols - 1; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
if (!(i % 2)) {
@@ -918,7 +913,7 @@ describe('Terminal', () => {
it('line of ¥ with combining odd', () => {
term.wraparoundMode = true;
term.buffer.x = 1;
term.write(Array(50).join('¥\u0301'));
term.writeSync(Array(50).join('¥\u0301'));
for (let i = 1; i < term.cols - 1; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
if (!(i % 2)) {
@@ -942,7 +937,7 @@ describe('Terminal', () => {
});
it('line of ¥ with combining even', () => {
term.wraparoundMode = true;
term.write(Array(50).join('¥\u0301'));
term.writeSync(Array(50).join('¥\u0301'));
for (let i = 0; i < term.cols; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
if (i % 2) {
@@ -963,7 +958,7 @@ describe('Terminal', () => {
it('line of surrogate fullwidth with combining odd', () => {
term.wraparoundMode = true;
term.buffer.x = 1;
term.write(Array(50).join('\ud843\ude6d\u0301'));
term.writeSync(Array(50).join('\ud843\ude6d\u0301'));
for (let i = 1; i < term.cols - 1; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
if (!(i % 2)) {
@@ -987,7 +982,7 @@ describe('Terminal', () => {
});
it('line of surrogate fullwidth with combining even', () => {
term.wraparoundMode = true;
term.write(Array(50).join('\ud843\ude6d\u0301'));
term.writeSync(Array(50).join('\ud843\ude6d\u0301'));
for (let i = 0; i < term.cols; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
if (i % 2) {
@@ -1010,11 +1005,11 @@ describe('Terminal', () => {
describe('insert mode', () => {
const cell = new CellData();
it('halfwidth - all', () => {
term.write(Array(9).join('0123456789').slice(-80));
term.writeSync(Array(9).join('0123456789').slice(-80));
term.buffer.x = 10;
term.buffer.y = 0;
term.insertMode = true;
term.write('abcde');
term.writeSync('abcde');
expect(term.buffer.lines.get(0).length).eql(term.cols);
expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('a');
expect(term.buffer.lines.get(0).loadCell(14, cell).getChars()).eql('e');
@@ -1022,11 +1017,11 @@ describe('Terminal', () => {
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql('4');
});
it('fullwidth - insert', () => {
term.write(Array(9).join('0123456789').slice(-80));
term.writeSync(Array(9).join('0123456789').slice(-80));
term.buffer.x = 10;
term.buffer.y = 0;
term.insertMode = true;
term.write('¥¥¥');
term.writeSync('¥¥¥');
expect(term.buffer.lines.get(0).length).eql(term.cols);
expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('¥');
expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('');
@@ -1035,16 +1030,16 @@ describe('Terminal', () => {
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql('3');
});
it('fullwidth - right border', () => {
term.write(Array(41).join('¥'));
term.writeSync(Array(41).join('¥'));
term.buffer.x = 10;
term.buffer.y = 0;
term.insertMode = true;
term.write('a');
term.writeSync('a');
expect(term.buffer.lines.get(0).length).eql(term.cols);
expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('a');
expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('¥');
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql(''); // fullwidth char got replaced
term.write('b');
term.writeSync('b');
expect(term.buffer.lines.get(0).length).eql(term.cols);
expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('b');
expect(term.buffer.lines.get(0).loadCell(12, cell).getChars()).eql('¥');
+15 -209
View File
@@ -62,25 +62,11 @@ import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions,
import { DirtyRowService } from 'common/services/DirtyRowService';
import { InstantiationService } from 'common/services/InstantiationService';
import { CoreMouseService } from 'common/services/CoreMouseService';
import { WriteBuffer } from 'common/input/WriteBuffer';
// Let it work inside Node.js for automated testing purposes.
const document = (typeof window !== 'undefined') ? window.document : null;
/**
* The amount of write requests to queue before sending an XOFF signal to the
* pty process. This number must be small in order for ^C and similar sequences
* to be responsive.
*/
const WRITE_BUFFER_PAUSE_THRESHOLD = 5;
/**
* The max number of ms to spend on writes before allowing the renderer to
* catch up with a 0ms setTimeout. A value of < 33 to keep us close to
* 30fps, and a value of < 16 to try to run at 60fps. Of course, the real FPS
* depends on the time it takes for the renderer to draw the frame.
*/
const WRITE_TIMEOUT_MS = 12;
const WRITE_BUFFER_LENGTH_THRESHOLD = 50;
export class Terminal extends Disposable implements ITerminal, IDisposable, IInputHandlingTerminal {
public textarea: HTMLTextAreaElement;
@@ -153,21 +139,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
public params: (string | number)[];
public currentParam: string | number;
// user input states
public writeBuffer: string[];
public writeBufferUtf8: Uint8Array[];
private _writeInProgress: boolean;
/**
* Whether _xterm.js_ sent XOFF in order to catch up with the pty process.
* This is a distinct state from writeStopped so that if the user requested
* XOFF via ^S that it will not automatically resume when the writeBuffer goes
* below threshold.
*/
private _xoffSentToCatchUp: boolean;
/** Whether writing has been stopped as a result of XOFF */
// private _writeStopped: boolean;
// write buffer
private _writeBuffer: WriteBuffer;
// Store if user went browsing history in scrollback
private _userScrolling: boolean;
@@ -258,6 +231,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._setupOptionsListeners();
this._setup();
this._writeBuffer = new WriteBuffer(data => this._inputHandler.parse(data));
}
public dispose(): void {
@@ -306,13 +281,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this.params = [];
this.currentParam = 0;
// user input states
this.writeBuffer = [];
this.writeBufferUtf8 = [];
this._writeInProgress = false;
this._xoffSentToCatchUp = false;
// this._writeStopped = false;
this._userScrolling = false;
// Register input handler and refire/handle events
@@ -604,8 +572,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// Performance: Add viewport and helper elements from the fragment
this.element.appendChild(fragment);
this._theme = this.options.theme;
this.options.theme = null;
this._theme = this.options.theme || this._theme;
this.options.theme = undefined;
this._colorManager = new ColorManager(document, this.options.allowTransparency);
this._colorManager.setTheme(this._theme);
@@ -1144,168 +1112,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
}
/**
* Writes raw utf8 bytes to the terminal.
* @param data UintArray with UTF8 bytes to write to the terminal.
*/
public writeUtf8(data: Uint8Array): void {
// Ensure the terminal isn't disposed
if (this._isDisposed) {
return;
}
// Ignore falsy data values
if (!data) {
return;
}
this.writeBufferUtf8.push(data);
// Send XOFF to pause the pty process if the write buffer becomes too large so
// xterm.js can catch up before more data is sent. This is necessary in order
// to keep signals such as ^C responsive.
if (this.options.useFlowControl && !this._xoffSentToCatchUp && this.writeBufferUtf8.length >= WRITE_BUFFER_PAUSE_THRESHOLD) {
// XOFF - stop pty pipe
// XON will be triggered by emulator before processing data chunk
this._coreService.triggerDataEvent(C0.DC3);
this._xoffSentToCatchUp = true;
}
if (!this._writeInProgress && this.writeBufferUtf8.length > 0) {
// Kick off a write which will write all data in sequence recursively
this._writeInProgress = true;
// Kick off an async innerWrite so more writes can come in while processing data
setTimeout(() => {
this._innerWriteUtf8();
});
}
}
protected _innerWriteUtf8(bufferOffset: number = 0): void {
// Ensure the terminal isn't disposed
if (this._isDisposed) {
this.writeBufferUtf8 = [];
}
const startTime = Date.now();
while (this.writeBufferUtf8.length > bufferOffset) {
const data = this.writeBufferUtf8[bufferOffset];
bufferOffset++;
// If XOFF was sent in order to catch up with the pty process, resume it if
// we reached the end of the writeBuffer to allow more data to come in.
if (this._xoffSentToCatchUp && this.writeBufferUtf8.length === bufferOffset) {
this._coreService.triggerDataEvent(C0.DC1);
this._xoffSentToCatchUp = false;
}
this._inputHandler.parseUtf8(data);
this.refresh(this._dirtyRowService.start, this._dirtyRowService.end);
if (Date.now() - startTime >= WRITE_TIMEOUT_MS) {
break;
}
}
if (this.writeBufferUtf8.length > bufferOffset) {
// Allow renderer to catch up before processing the next batch
// trim already processed chunks if we are above threshold
if (bufferOffset > WRITE_BUFFER_LENGTH_THRESHOLD) {
this.writeBufferUtf8 = this.writeBufferUtf8.slice(bufferOffset);
bufferOffset = 0;
}
setTimeout(() => this._innerWriteUtf8(bufferOffset), 0);
} else {
this._writeInProgress = false;
this.writeBufferUtf8 = [];
}
}
/**
* Writes text to the terminal.
* @param data The text to write to the terminal.
*/
public write(data: string): void {
// Ensure the terminal isn't disposed
if (this._isDisposed) {
return;
}
// Ignore falsy data values (including the empty string)
if (!data) {
return;
}
this.writeBuffer.push(data);
// Send XOFF to pause the pty process if the write buffer becomes too large so
// xterm.js can catch up before more data is sent. This is necessary in order
// to keep signals such as ^C responsive.
if (this.options.useFlowControl && !this._xoffSentToCatchUp && this.writeBuffer.length >= WRITE_BUFFER_PAUSE_THRESHOLD) {
// XOFF - stop pty pipe
// XON will be triggered by emulator before processing data chunk
this._coreService.triggerDataEvent(C0.DC3);
this._xoffSentToCatchUp = true;
}
if (!this._writeInProgress && this.writeBuffer.length > 0) {
// Kick off a write which will write all data in sequence recursively
this._writeInProgress = true;
// Kick off an async innerWrite so more writes can come in while processing data
setTimeout(() => {
this._innerWrite();
});
}
}
protected _innerWrite(bufferOffset: number = 0): void {
// Ensure the terminal isn't disposed
if (this._isDisposed) {
this.writeBuffer = [];
}
const startTime = Date.now();
while (this.writeBuffer.length > bufferOffset) {
const data = this.writeBuffer[bufferOffset];
bufferOffset++;
// If XOFF was sent in order to catch up with the pty process, resume it if
// we reached the end of the writeBuffer to allow more data to come in.
if (this._xoffSentToCatchUp && this.writeBuffer.length === bufferOffset) {
this._coreService.triggerDataEvent(C0.DC1);
this._xoffSentToCatchUp = false;
}
this._inputHandler.parse(data);
this.refresh(this._dirtyRowService.start, this._dirtyRowService.end);
if (Date.now() - startTime >= WRITE_TIMEOUT_MS) {
break;
}
}
if (this.writeBuffer.length > bufferOffset) {
// Allow renderer to catch up before processing the next batch
// trim already processed chunks if we are above threshold
if (bufferOffset > WRITE_BUFFER_LENGTH_THRESHOLD) {
this.writeBuffer = this.writeBuffer.slice(bufferOffset);
bufferOffset = 0;
}
setTimeout(() => this._innerWrite(bufferOffset), 0);
} else {
this._writeInProgress = false;
this.writeBuffer = [];
}
}
/**
* Writes text to the terminal, followed by a break line character (\n).
* @param data The text to write to the terminal.
*/
public writeln(data: string): void {
this.write(data + '\r\n');
}
public paste(data: string): void {
paste(data, this.textarea, this.bracketedPasteMode, this._coreService);
}
@@ -1743,10 +1549,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
const customKeyEventHandler = this._customKeyEventHandler;
const inputHandler = this._inputHandler;
const cursorState = this.cursorState;
const writeBuffer = this.writeBuffer;
const writeBufferUtf8 = this.writeBufferUtf8;
const writeInProgress = this._writeInProgress;
const xoffSentToCatchUp = this._xoffSentToCatchUp;
const userScrolling = this._userScrolling;
this._setup();
@@ -1761,10 +1563,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._customKeyEventHandler = customKeyEventHandler;
this._inputHandler = inputHandler;
this.cursorState = cursorState;
this.writeBuffer = writeBuffer;
this.writeBufferUtf8 = writeBufferUtf8;
this._writeInProgress = writeInProgress;
this._xoffSentToCatchUp = xoffSentToCatchUp;
this._userScrolling = userScrolling;
// do a full screen refresh
@@ -1795,6 +1593,14 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// return this.options.bellStyle === 'sound' ||
// this.options.bellStyle === 'both';
}
public write(data: string | Uint8Array, callback?: () => void): void {
this._writeBuffer.write(data, callback);
}
public writeSync(data: string | Uint8Array): void {
this._writeBuffer.writeSync(data);
}
}
/**
-4
View File
@@ -19,10 +19,6 @@ import { IParams, IFunctionIdentifier } from 'common/parser/Types';
import { ISelectionService } from 'browser/services/Services';
export class TestTerminal extends Terminal {
writeSync(data: string): void {
this.writeBuffer.push(data);
this._innerWrite();
}
keyDown(ev: any): boolean { return this._keyDown(ev); }
keyPress(ev: any): boolean { return this._keyPress(ev); }
}
+2 -6
View File
@@ -73,8 +73,7 @@ export interface ICompositionHelper {
* Calls the parser and handles actions generated by the parser.
*/
export interface IInputHandler {
parse(data: string): void;
parseUtf8(data: Uint8Array): void;
parse(data: string | Uint8Array): void;
print(data: Uint32Array, start: number, end: number): void;
/** C0 BEL */ bell(): void;
@@ -155,7 +154,6 @@ export interface IInputHandler {
export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor {
screenElement: HTMLElement;
browser: IBrowser;
writeBuffer: string[];
cursorHidden: boolean;
cursorState: number;
buffer: IBuffer;
@@ -196,7 +194,6 @@ export interface IPublicTerminal extends IDisposable {
blur(): void;
focus(): void;
resize(columns: number, rows: number): void;
writeln(data: string): void;
open(parent: HTMLElement): void;
attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void;
addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable;
@@ -222,8 +219,7 @@ export interface IPublicTerminal extends IDisposable {
scrollToBottom(): void;
scrollToLine(line: number): void;
clear(): void;
write(data: string): void;
writeUtf8(data: Uint8Array): void;
write(data: string | Uint8Array, callback?: () => void): void;
paste(data: string): void;
refresh(start: number, end: number): void;
reset(): void;
+5 -10
View File
@@ -316,23 +316,18 @@ export function evaluateKeyboardEvent(
if (ev.keyCode >= 65 && ev.keyCode <= 90) {
result.key = String.fromCharCode(ev.keyCode - 64);
} else if (ev.keyCode === 32) {
// NUL
result.key = String.fromCharCode(0);
result.key = C0.NUL;
} else if (ev.keyCode >= 51 && ev.keyCode <= 55) {
// escape, file sep, group sep, record sep, unit sep
result.key = String.fromCharCode(ev.keyCode - 51 + 27);
} else if (ev.keyCode === 56) {
// delete
result.key = String.fromCharCode(127);
result.key = C0.DEL;
} else if (ev.keyCode === 219) {
// ^[ - Control Sequence Introducer (CSI)
result.key = String.fromCharCode(27);
result.key = C0.ESC;
} else if (ev.keyCode === 220) {
// ^\ - String Terminator (ST)
result.key = String.fromCharCode(28);
result.key = C0.FS;
} else if (ev.keyCode === 221) {
// ^] - Operating System Command (OSC)
result.key = String.fromCharCode(29);
result.key = C0.GS;
}
} else if ((!isMac || macOptionIsMeta) && ev.altKey && !ev.metaKey) {
// On macOS this is a third level shift when !macOptionIsMeta. Use <Esc> instead.

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