Merge remote-tracking branch 'upstream/master' into 459_parser__on_460
This commit is contained in:
+6
-6
@@ -46,13 +46,13 @@ export class Viewport {
|
|||||||
*/
|
*/
|
||||||
private refresh(): void {
|
private refresh(): void {
|
||||||
if (this.charMeasure.height > 0) {
|
if (this.charMeasure.height > 0) {
|
||||||
var rowHeightChanged = this.charMeasure.height !== this.currentRowHeight;
|
const rowHeightChanged = this.charMeasure.height !== this.currentRowHeight;
|
||||||
if (rowHeightChanged) {
|
if (rowHeightChanged) {
|
||||||
this.currentRowHeight = this.charMeasure.height;
|
this.currentRowHeight = this.charMeasure.height;
|
||||||
this.viewportElement.style.lineHeight = this.charMeasure.height + 'px';
|
this.viewportElement.style.lineHeight = this.charMeasure.height + 'px';
|
||||||
this.terminal.rowContainer.style.lineHeight = this.charMeasure.height + 'px';
|
this.terminal.rowContainer.style.lineHeight = this.charMeasure.height + 'px';
|
||||||
}
|
}
|
||||||
var viewportHeightChanged = this.lastRecordedViewportHeight !== this.terminal.rows;
|
const viewportHeightChanged = this.lastRecordedViewportHeight !== this.terminal.rows;
|
||||||
if (rowHeightChanged || viewportHeightChanged) {
|
if (rowHeightChanged || viewportHeightChanged) {
|
||||||
this.lastRecordedViewportHeight = this.terminal.rows;
|
this.lastRecordedViewportHeight = this.terminal.rows;
|
||||||
this.viewportElement.style.height = this.charMeasure.height * this.terminal.rows + 'px';
|
this.viewportElement.style.height = this.charMeasure.height * this.terminal.rows + 'px';
|
||||||
@@ -80,7 +80,7 @@ export class Viewport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sync scrollTop
|
// Sync scrollTop
|
||||||
var scrollTop = this.terminal.ydisp * this.currentRowHeight;
|
const scrollTop = this.terminal.ydisp * this.currentRowHeight;
|
||||||
if (this.viewportElement.scrollTop !== scrollTop) {
|
if (this.viewportElement.scrollTop !== scrollTop) {
|
||||||
this.viewportElement.scrollTop = scrollTop;
|
this.viewportElement.scrollTop = scrollTop;
|
||||||
}
|
}
|
||||||
@@ -92,8 +92,8 @@ export class Viewport {
|
|||||||
* @param ev The scroll event.
|
* @param ev The scroll event.
|
||||||
*/
|
*/
|
||||||
private onScroll(ev: Event) {
|
private onScroll(ev: Event) {
|
||||||
var newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight);
|
const newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight);
|
||||||
var diff = newRow - this.terminal.ydisp;
|
const diff = newRow - this.terminal.ydisp;
|
||||||
this.terminal.scrollDisp(diff, true);
|
this.terminal.scrollDisp(diff, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ export class Viewport {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Fallback to WheelEvent.DOM_DELTA_PIXEL
|
// Fallback to WheelEvent.DOM_DELTA_PIXEL
|
||||||
var multiplier = 1;
|
let multiplier = 1;
|
||||||
if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
|
if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
|
||||||
multiplier = this.currentRowHeight;
|
multiplier = this.currentRowHeight;
|
||||||
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
|
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
|
||||||
|
|||||||
@@ -92,7 +92,11 @@ describe('xterm output comparison', function() {
|
|||||||
var from_pty = pty_write_read(in_file);
|
var from_pty = pty_write_read(in_file);
|
||||||
// uncomment this to get log from terminal
|
// uncomment this to get log from terminal
|
||||||
//console.log = function(){};
|
//console.log = function(){};
|
||||||
xterm.write(from_pty);
|
|
||||||
|
// Perform a synchronous .write(data)
|
||||||
|
xterm.writeBuffer.push(from_pty);
|
||||||
|
xterm.innerWrite();
|
||||||
|
|
||||||
var from_emulator = terminalToString(xterm);
|
var from_emulator = terminalToString(xterm);
|
||||||
console.log = CONSOLE_LOG;
|
console.log = CONSOLE_LOG;
|
||||||
var expected = fs.readFileSync(filename.split('.')[0] + '.text', 'utf8');
|
var expected = fs.readFileSync(filename.split('.')[0] + '.text', 'utf8');
|
||||||
|
|||||||
@@ -14,6 +14,15 @@ describe('xterm.js', function() {
|
|||||||
xterm.compositionHelper = {
|
xterm.compositionHelper = {
|
||||||
keydown: function(){ return true; }
|
keydown: function(){ return true; }
|
||||||
};
|
};
|
||||||
|
// Force synchronous refreshes
|
||||||
|
xterm.queueRefresh = function(start, end) {
|
||||||
|
xterm.refresh(start, end);
|
||||||
|
};
|
||||||
|
// Force synchronous writes
|
||||||
|
xterm.write = function(data) {
|
||||||
|
xterm.writeBuffer.push(data);
|
||||||
|
xterm.innerWrite();
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getOption', function() {
|
describe('getOption', function() {
|
||||||
|
|||||||
@@ -28,24 +28,29 @@ export class CharMeasure extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public measure(): void {
|
public measure(): void {
|
||||||
const oldWidth = this._width;
|
|
||||||
const oldHeight = this._height;
|
|
||||||
|
|
||||||
if (!this._measureElement) {
|
if (!this._measureElement) {
|
||||||
this._measureElement = document.createElement('span');
|
this._measureElement = document.createElement('span');
|
||||||
this._measureElement.style.position = 'absolute';
|
this._measureElement.style.position = 'absolute';
|
||||||
this._measureElement.style.top = '0';
|
this._measureElement.style.top = '0';
|
||||||
this._measureElement.style.left = '-9999em';
|
this._measureElement.style.left = '-9999em';
|
||||||
this._measureElement.textContent = 'W';
|
this._measureElement.textContent = 'W';
|
||||||
|
this._parentElement.appendChild(this._measureElement);
|
||||||
|
// Perform _doMeasure async if the element was just attached as sometimes
|
||||||
|
// getBoundingClientRect does not return accurate values without this.
|
||||||
|
setTimeout(() => this._doMeasure(), 0);
|
||||||
|
} else {
|
||||||
|
this._doMeasure();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this._parentElement.appendChild(this._measureElement);
|
private _doMeasure(): void {
|
||||||
|
const oldWidth = this._width;
|
||||||
|
const oldHeight = this._height;
|
||||||
const geometry = this._measureElement.getBoundingClientRect();
|
const geometry = this._measureElement.getBoundingClientRect();
|
||||||
this._width = geometry.width;
|
|
||||||
this._height = geometry.height;
|
|
||||||
this._parentElement.removeChild(this._measureElement);
|
|
||||||
|
|
||||||
if (this._width !== oldWidth || this._height !== oldHeight) {
|
if (this._width !== geometry.width || this._height !== geometry.height) {
|
||||||
|
this._width = geometry.width;
|
||||||
|
this._height = geometry.height;
|
||||||
this.emit('charsizechanged');
|
this.emit('charsizechanged');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+121
-23
@@ -42,6 +42,26 @@ var document = (typeof window != 'undefined') ? window.document : null;
|
|||||||
*/
|
*/
|
||||||
var normal = 0, escaped = 1, csi = 2, osc = 3, charset = 4, dcs = 5, ignore = 6;
|
var normal = 0, escaped = 1, csi = 2, osc = 3, charset = 4, dcs = 5, ignore = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
var WRITE_BUFFER_PAUSE_THRESHOLD = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of writes to perform in a single batch before allowing the
|
||||||
|
* renderer to catch up with a 0ms setTimeout.
|
||||||
|
*/
|
||||||
|
var WRITE_BATCH_SIZE = 300;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of refresh frames to skip when the write buffer is non-
|
||||||
|
* empty. Note that these frames may be intermingled with frames that are
|
||||||
|
* skipped via requestAnimationFrame's mechanism.
|
||||||
|
*/
|
||||||
|
var MAX_REFRESH_FRAME_SKIP = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminal
|
* Terminal
|
||||||
*/
|
*/
|
||||||
@@ -203,6 +223,22 @@ function Terminal(options) {
|
|||||||
this.inputHandler = new InputHandler(this);
|
this.inputHandler = new InputHandler(this);
|
||||||
this.parser = new Parser(this.inputHandler, this);
|
this.parser = new Parser(this.inputHandler, this);
|
||||||
|
|
||||||
|
// user input states
|
||||||
|
this.writeBuffer = [];
|
||||||
|
this.writeInProgress = false;
|
||||||
|
this.refreshFramesSkipped = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
this.xoffSentToCatchUp = false;
|
||||||
|
|
||||||
|
/** Whether writing has been stopped as a result of XOFF */
|
||||||
|
this.writeStopped = false;
|
||||||
|
|
||||||
// leftover surrogate high from previous write invocation
|
// leftover surrogate high from previous write invocation
|
||||||
this.surrogate_high = '';
|
this.surrogate_high = '';
|
||||||
|
|
||||||
@@ -1028,27 +1064,36 @@ Terminal.prototype.queueRefresh = function(start, end) {
|
|||||||
Terminal.prototype.refreshLoop = function() {
|
Terminal.prototype.refreshLoop = function() {
|
||||||
// Don't refresh if there were no row changes
|
// Don't refresh if there were no row changes
|
||||||
if (this.refreshRowsQueue.length > 0) {
|
if (this.refreshRowsQueue.length > 0) {
|
||||||
var start;
|
// Skip MAX_REFRESH_FRAME_SKIP frames if the writeBuffer is non-empty as it
|
||||||
var end;
|
// will need to be immediately refreshed anyway. This saves a lot of
|
||||||
if (this.refreshRowsQueue.length > 4) {
|
// rendering time as the viewport DOM does not need to be refreshed, no
|
||||||
// Just do a full refresh when 5+ refreshes are queued
|
// scroll events, no layouts, etc.
|
||||||
start = 0;
|
var skipFrame = this.writeBuffer.length > 0 && this.refreshFramesSkipped++ <= MAX_REFRESH_FRAME_SKIP;
|
||||||
end = this.rows - 1;
|
|
||||||
} else {
|
if (!skipFrame) {
|
||||||
// Get start and end rows that need refreshing
|
this.refreshFramesSkipped = 0;
|
||||||
start = this.refreshRowsQueue[0].start;
|
var start;
|
||||||
end = this.refreshRowsQueue[0].end;
|
var end;
|
||||||
for (var i = 1; i < this.refreshRowsQueue.length; i++) {
|
if (this.refreshRowsQueue.length > 4) {
|
||||||
if (this.refreshRowsQueue[i].start < start) {
|
// Just do a full refresh when 5+ refreshes are queued
|
||||||
start = this.refreshRowsQueue[i].start;
|
start = 0;
|
||||||
}
|
end = this.rows - 1;
|
||||||
if (this.refreshRowsQueue[i].end > end) {
|
} else {
|
||||||
end = this.refreshRowsQueue[i].end;
|
// Get start and end rows that need refreshing
|
||||||
|
start = this.refreshRowsQueue[0].start;
|
||||||
|
end = this.refreshRowsQueue[0].end;
|
||||||
|
for (var i = 1; i < this.refreshRowsQueue.length; i++) {
|
||||||
|
if (this.refreshRowsQueue[i].start < start) {
|
||||||
|
start = this.refreshRowsQueue[i].start;
|
||||||
|
}
|
||||||
|
if (this.refreshRowsQueue[i].end > end) {
|
||||||
|
end = this.refreshRowsQueue[i].end;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.refreshRowsQueue = [];
|
||||||
|
this.refresh(start, end);
|
||||||
}
|
}
|
||||||
this.refreshRowsQueue = [];
|
|
||||||
this.refresh(start, end);
|
|
||||||
}
|
}
|
||||||
window.requestAnimationFrame(this.refreshLoop.bind(this));
|
window.requestAnimationFrame(this.refreshLoop.bind(this));
|
||||||
}
|
}
|
||||||
@@ -1368,13 +1413,59 @@ Terminal.prototype.scrollToBottom = function() {
|
|||||||
* @param {string} text The text to write to the terminal.
|
* @param {string} text The text to write to the terminal.
|
||||||
*/
|
*/
|
||||||
Terminal.prototype.write = function(data) {
|
Terminal.prototype.write = function(data) {
|
||||||
this.refreshStart = this.y;
|
this.writeBuffer.push(data);
|
||||||
this.refreshEnd = this.y;
|
|
||||||
|
|
||||||
this.parser.parse(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.xoffSentToCatchUp && this.writeBuffer.length >= WRITE_BUFFER_PAUSE_THRESHOLD) {
|
||||||
|
// XOFF - stop pty pipe
|
||||||
|
// XON will be triggered by emulator before processing data chunk
|
||||||
|
this.send(C0.DC3);
|
||||||
|
this.xoffSentToCatchUp = true;
|
||||||
|
}
|
||||||
|
|
||||||
this.updateRange(this.y);
|
if (!this.writeInProgress && this.writeBuffer.length > 0) {
|
||||||
this.queueRefresh(this.refreshStart, this.refreshEnd);
|
// 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
|
||||||
|
var self = this;
|
||||||
|
setTimeout(function () {
|
||||||
|
self.innerWrite();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Terminal.prototype.innerWrite = function() {
|
||||||
|
var writeBatch = this.writeBuffer.splice(0, WRITE_BATCH_SIZE);
|
||||||
|
while (writeBatch.length > 0) {
|
||||||
|
var data = writeBatch.shift();
|
||||||
|
var l = data.length, i = 0, j, cs, ch, code, low, ch_width, row;
|
||||||
|
|
||||||
|
// If XOFF was sent in order to catch up with the pty process, resume it if
|
||||||
|
// the writeBuffer is empty to allow more data to come in.
|
||||||
|
if (this.xoffSentToCatchUp && writeBatch.length === 0 && this.writeBuffer.length === 0) {
|
||||||
|
this.send(C0.DC1);
|
||||||
|
this.xoffSentToCatchUp = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.refreshStart = this.y;
|
||||||
|
this.refreshEnd = this.y;
|
||||||
|
|
||||||
|
this.parser.parse(data);
|
||||||
|
|
||||||
|
this.updateRange(this.y);
|
||||||
|
this.queueRefresh(this.refreshStart, this.refreshEnd);
|
||||||
|
}
|
||||||
|
if (this.writeBuffer.length > 0) {
|
||||||
|
// Allow renderer to catch up before processing the next batch
|
||||||
|
var self = this;
|
||||||
|
setTimeout(function () {
|
||||||
|
self.innerWrite();
|
||||||
|
}, 0);
|
||||||
|
} else {
|
||||||
|
this.writeInProgress = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1418,6 +1509,12 @@ Terminal.prototype.keyDown = function(ev) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
var result = this.evaluateKeyEscapeSequence(ev);
|
var result = this.evaluateKeyEscapeSequence(ev);
|
||||||
|
|
||||||
|
if (result.key === C0.DC3) { // XOFF
|
||||||
|
this.writeStopped = true;
|
||||||
|
} else if (result.key === C0.DC1) { // XON
|
||||||
|
this.writeStopped = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (result.scrollDisp) {
|
if (result.scrollDisp) {
|
||||||
this.scrollDisp(result.scrollDisp);
|
this.scrollDisp(result.scrollDisp);
|
||||||
return this.cancel(ev, true);
|
return this.cancel(ev, true);
|
||||||
@@ -1723,6 +1820,7 @@ Terminal.prototype.evaluateKeyEscapeSequence = function(ev) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user