Merge remote-tracking branch 'upstream/master' into 439_wide_char_class

This commit is contained in:
Daniel Imms
2017-01-08 16:32:51 -08:00
7 changed files with 111 additions and 82 deletions
+10
View File
@@ -1,6 +1,11 @@
import { assert } from 'chai';
import { Viewport } from './Viewport';
class MockWindow {
// Disable refreshLoop in test
public requestAnimationFrame() { }
}
describe('Viewport', () => {
let terminal;
let viewportElement;
@@ -11,6 +16,7 @@ describe('Viewport', () => {
const CHARACTER_HEIGHT = 10;
beforeEach(() => {
(<any>global).window = new MockWindow();
terminal = {
lines: [],
rows: 0,
@@ -67,10 +73,14 @@ describe('Viewport', () => {
terminal.rows = 1;
assert.equal(scrollAreaElement.style.height, 0 * CHARACTER_HEIGHT + 'px');
viewport.syncScrollArea();
assert.ok(viewport.isRefreshQueued);
viewport.refresh();
assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
assert.equal(scrollAreaElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
terminal.lines.push('');
viewport.syncScrollArea();
assert.ok(viewport.isRefreshQueued);
viewport.refresh();
assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
assert.equal(scrollAreaElement.style.height, 2 * CHARACTER_HEIGHT + 'px');
});
+17 -3
View File
@@ -13,6 +13,7 @@ export class Viewport {
private currentRowHeight: number;
private lastRecordedBufferLength: number;
private lastRecordedViewportHeight: number;
private isRefreshQueued: boolean;
/**
* Creates a new Viewport.
@@ -30,12 +31,25 @@ export class Viewport {
this.currentRowHeight = 0;
this.lastRecordedBufferLength = 0;
this.lastRecordedViewportHeight = 0;
this.isRefreshQueued = false;
this.terminal.on('scroll', this.syncScrollArea.bind(this));
this.terminal.on('resize', this.syncScrollArea.bind(this));
this.viewportElement.addEventListener('scroll', this.onScroll.bind(this));
this.syncScrollArea();
this.refreshLoop();
}
/**
* Queues a refresh to be done on next animation frame.
*/
private refreshLoop(): void {
if (this.isRefreshQueued) {
this.refresh();
this.isRefreshQueued = false;
}
window.requestAnimationFrame(this.refreshLoop.bind(this));
}
/**
@@ -68,14 +82,14 @@ export class Viewport {
if (this.lastRecordedBufferLength !== this.terminal.lines.length) {
// If buffer height changed
this.lastRecordedBufferLength = this.terminal.lines.length;
this.refresh();
this.isRefreshQueued = true;
} else if (this.lastRecordedViewportHeight !== this.terminal.rows) {
// If viewport height changed
this.refresh();
this.isRefreshQueued = true;
} else {
// If size has changed, refresh viewport
if (this.charMeasure.height !== this.currentRowHeight) {
this.refresh();
this.isRefreshQueued = true;
}
}
-22
View File
@@ -1,22 +0,0 @@
/**
* Attributes and methods to help with identifying the current browser and platform.
* @module xterm/utils/Browser
* @license MIT
*/
import { contains } from './Generic.js';
let isNode = (typeof navigator == 'undefined') ? true : false;
let userAgent = (isNode) ? 'node' : navigator.userAgent;
let platform = (isNode) ? 'node' : navigator.platform;
export let isFirefox = !!~userAgent.indexOf('Firefox');
export let isMSIE = !!~userAgent.indexOf('MSIE') || !!~userAgent.indexOf('Trident');
// Find the users platform. We use this to interpret the meta key
// and ISO third level shifts.
// http://stackoverflow.com/q/19877924/577598
export let isMac = contains(['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], platform);
export let isIpad = platform === 'iPad';
export let isIphone = platform === 'iPhone';
export let isMSWindows = contains(['Windows', 'Win16', 'Win32', 'WinCE'], platform);
+22
View File
@@ -0,0 +1,22 @@
/**
* Attributes and methods to help with identifying the current browser and platform.
* @module xterm/utils/Browser
* @license MIT
*/
import { contains } from './Generic';
const isNode = (typeof navigator === 'undefined') ? true : false;
const userAgent = (isNode) ? 'node' : navigator.userAgent;
const platform = (isNode) ? 'node' : navigator.platform;
export const isFirefox = !!~userAgent.indexOf('Firefox');
export const isMSIE = !!~userAgent.indexOf('MSIE') || !!~userAgent.indexOf('Trident');
// Find the users platform. We use this to interpret the meta key
// and ISO third level shifts.
// http://stackoverflow.com/q/19877924/577598
export const isMac = contains(['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], platform);
export const isIpad = platform === 'iPad';
export const isIphone = platform === 'iPhone';
export const isMSWindows = contains(['Windows', 'Win16', 'Win32', 'WinCE'], platform);
@@ -9,6 +9,6 @@
* @param {Array} array The array to search for the given element.
* @param {Object} el The element to look for into the array
*/
export let contains = function(arr, el) {
export function contains(arr: any[], el: any) {
return arr.indexOf(el) >= 0;
};
+56 -56
View File
@@ -138,14 +138,8 @@ function Terminal(options) {
*/
this.y = 0;
/**
* Used to debounce the refresh function
*/
this.isRefreshing = false;
/**
* Whether there is a full terminal refresh queued
*/
/** A queue of the rows to be refreshed */
this.refreshRowsQueue = [];
this.cursorState = 0;
this.cursorHidden = false;
@@ -408,7 +402,7 @@ Terminal.prototype.blur = function() {
*/
Terminal.bindBlur = function (term) {
on(term.textarea, 'blur', function (ev) {
term.refresh(term.y, term.y);
term.queueRefresh(term.y, term.y);
if (term.sendFocus) {
term.send('\x1b[O');
}
@@ -590,8 +584,9 @@ Terminal.prototype.open = function(parent) {
this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure);
// Draw the screen.
this.refresh(0, this.rows - 1);
// Setup loop that draws to screen
this.queueRefresh(0, this.rows - 1);
this.refreshLoop();
// Initialize global actions that
// need to be taken on the document.
@@ -1009,6 +1004,48 @@ Terminal.flags = {
INVISIBLE: 16
}
/**
* Queues a refresh between two rows (inclusive), to be done on next animation
* frame.
* @param {number} start The start row.
* @param {number} end The end row.
*/
Terminal.prototype.queueRefresh = function(start, end) {
this.refreshRowsQueue.push({ start: start, end: end });
}
/**
* Performs the refresh loop callback, calling refresh only if a refresh is
* necessary before queueing up the next one.
*/
Terminal.prototype.refreshLoop = function() {
// Don't refresh if there were no row changes
if (this.refreshRowsQueue.length > 0) {
var start;
var end;
if (this.refreshRowsQueue.length > 4) {
// Just do a full refresh when 5+ refreshes are queued
start = 0;
end = this.rows - 1;
} else {
// 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);
}
window.requestAnimationFrame(this.refreshLoop.bind(this));
}
/**
* Refreshes (re-renders) terminal content within two rows (inclusive)
*
@@ -1029,47 +1066,10 @@ Terminal.flags = {
*
* @param {number} start The row to start from (between 0 and terminal's height terminal - 1)
* @param {number} end The row to end at (between fromRow and terminal's height terminal - 1)
* @param {boolean} queue Whether the refresh should ran right now or be queued
*/
Terminal.prototype.refresh = function(start, end, queue) {
Terminal.prototype.refresh = function(start, end) {
var self = this;
// queue defaults to true
queue = (typeof queue == 'undefined') ? true : queue;
/**
* The refresh queue allows refresh to execute only approximately 30 times a second. For
* commands that pass a significant amount of output to the write function, this prevents the
* terminal from maxing out the CPU and making the UI unresponsive. While commands can still
* run beyond what they do on the terminal, it is far better with a debounce in place as
* every single terminal manipulation does not need to be constructed in the DOM.
*
* A side-effect of this is that it makes ^C to interrupt a process seem more responsive.
*/
if (queue) {
// If refresh should be queued, order the refresh and return.
if (this._refreshIsQueued) {
// If a refresh has already been queued, just order a full refresh next
this._fullRefreshNext = true;
} else {
setTimeout(function () {
self.refresh(start, end, false);
}, 34)
this._refreshIsQueued = true;
}
return;
}
// If refresh should be run right now (not be queued), release the lock
this._refreshIsQueued = false;
// If multiple refreshes were requested, make a full refresh.
if (this._fullRefreshNext) {
start = 0;
end = this.rows - 1;
this._fullRefreshNext = false // reset lock
}
var x, y, i, line, out, ch, ch_width, width, data, attr, bg, fg, flags, row, parent, focused = document.activeElement;
// If this is a big refresh, remove the terminal rows from the DOM for faster calculations
@@ -1242,7 +1242,7 @@ Terminal.prototype.refresh = function(start, end, queue) {
Terminal.prototype.showCursor = function() {
if (!this.cursorState) {
this.cursorState = 1;
this.refresh(this.y, this.y);
this.queueRefresh(this.y, this.y);
}
};
@@ -1329,7 +1329,7 @@ Terminal.prototype.scrollDisp = function(disp, suppressScrollEvent) {
this.emit('scroll', this.ydisp);
}
this.refresh(0, this.rows - 1);
this.queueRefresh(0, this.rows - 1);
};
/**
@@ -2397,7 +2397,7 @@ Terminal.prototype.write = function(data) {
}
this.updateRange(this.y);
this.refresh(this.refreshStart, this.refreshEnd);
this.queueRefresh(this.refreshStart, this.refreshEnd);
};
/**
@@ -2965,7 +2965,7 @@ Terminal.prototype.resize = function(x, y) {
this.charMeasure.measure();
this.refresh(0, this.rows - 1);
this.queueRefresh(0, this.rows - 1);
this.normal = null;
@@ -3094,7 +3094,7 @@ Terminal.prototype.clear = function() {
for (var i = 1; i < this.rows; i++) {
this.lines.push(this.blankLine());
}
this.refresh(0, this.rows - 1);
this.queueRefresh(0, this.rows - 1);
this.emit('scroll', this.ydisp);
};
@@ -3225,7 +3225,7 @@ Terminal.prototype.reset = function() {
var customKeydownHandler = this.customKeydownHandler;
Terminal.call(this, this.options);
this.customKeydownHandler = customKeydownHandler;
this.refresh(0, this.rows - 1);
this.queueRefresh(0, this.rows - 1);
this.viewport.syncScrollArea();
};
@@ -4344,7 +4344,7 @@ Terminal.prototype.resetMode = function(params) {
// this.x = this.savedX;
// this.y = this.savedY;
// }
this.refresh(0, this.rows - 1);
this.queueRefresh(0, this.rows - 1);
this.viewport.syncScrollArea();
this.showCursor();
}