jupyterlab-emrys
Version:
A computational environment for Jupyter. Powered by Emrys
396 lines (395 loc) • 12.7 kB
JavaScript
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var phosphor_domutil_1 = require('phosphor-domutil');
var phosphor_messaging_1 = require('phosphor-messaging');
var phosphor_widget_1 = require('phosphor-widget');
var Xterm = require('xterm');
/**
* The class name added to a terminal widget.
*/
var TERMINAL_CLASS = 'jp-TerminalWidget';
/**
* The class name added to a terminal body.
*/
var TERMINAL_BODY_CLASS = 'jp-TerminalWidget-body';
/**
* The number of rows to use in the dummy terminal.
*/
var DUMMY_ROWS = 24;
/**
* The number of cols to use in the dummy terminal.
*/
var DUMMY_COLS = 80;
/**
* A widget which manages a terminal session.
*/
var TerminalWidget = (function (_super) {
__extends(TerminalWidget, _super);
/**
* Construct a new terminal widget.
*
* @param options - The terminal configuration options.
*/
function TerminalWidget(options) {
if (options === void 0) { options = {}; }
_super.call(this);
this._term = null;
this._sheet = null;
this._dummyTerm = null;
this._fontSize = -1;
this._dirty = false;
this._rowHeight = -1;
this._colWidth = -1;
this._background = '';
this._color = '';
this._box = null;
this._session = null;
this.addClass(TERMINAL_CLASS);
// Create the xterm, dummy terminal, and private style sheet.
this._term = new Xterm(Private.getConfig(options));
this._initializeTerm();
this._dummyTerm = Private.createDummyTerm();
this._sheet = document.createElement('style');
this.node.appendChild(this._sheet);
// Initialize settings.
this.fontSize = options.fontSize || 14;
this.background = options.background || 'black';
this.color = options.color || 'white';
this.id = "jp-TerminalWidget-" + Private.id++;
this.title.text = 'Terminal';
Xterm.brokenBold = true;
}
Object.defineProperty(TerminalWidget.prototype, "session", {
/**
* The terminal session associated with the widget.
*/
get: function () {
return this._session;
},
set: function (value) {
if (this._session && !this._session.isDisposed) {
this._session.messageReceived.disconnect(this._onMessage, this);
}
this._session = value;
this._session.messageReceived.connect(this._onMessage, this);
this.title.text = "Terminal " + this._session.name;
this._resizeTerminal(-1, -1);
},
enumerable: true,
configurable: true
});
Object.defineProperty(TerminalWidget.prototype, "fontSize", {
/**
* Get the font size of the terminal in pixels.
*/
get: function () {
return this._fontSize;
},
/**
* Set the font size of the terminal in pixels.
*/
set: function (size) {
this._fontSize = size;
this._term.element.style.fontSize = size + "px";
this._snapTermSizing();
},
enumerable: true,
configurable: true
});
Object.defineProperty(TerminalWidget.prototype, "background", {
/**
* Get the background color of the terminal.
*/
get: function () {
return this._background;
},
/**
* Set the background color of the terminal.
*/
set: function (value) {
this._background = value;
this.update();
},
enumerable: true,
configurable: true
});
Object.defineProperty(TerminalWidget.prototype, "color", {
/**
* Get the text color of the terminal.
*/
get: function () {
return this._color;
},
/**
* Set the text color of the terminal.
*/
set: function (value) {
this._color = value;
this.update();
},
enumerable: true,
configurable: true
});
Object.defineProperty(TerminalWidget.prototype, "visualBell", {
/**
* Get whether the bell is shown.
*/
get: function () {
return this._term.visualBell;
},
/**
* Set whether the bell is shown.
*/
set: function (value) {
this._term.visualBell = value;
},
enumerable: true,
configurable: true
});
Object.defineProperty(TerminalWidget.prototype, "popOnBell", {
/**
* Get whether to focus on a bell event.
*/
get: function () {
return this._term.popOnBell;
},
/**
* Set whether to focus on a bell event.
*/
set: function (value) {
this._term.popOnBell = value;
},
enumerable: true,
configurable: true
});
Object.defineProperty(TerminalWidget.prototype, "scrollback", {
/**
* Get the size of the scrollback buffer in the terminal.
*/
get: function () {
return this._term.scrollback;
},
/**
* Set the size of the scrollback buffer in the terminal.
*/
set: function (value) {
this._term.scrollback = value;
},
enumerable: true,
configurable: true
});
/**
* Dispose of the resources held by the terminal widget.
*/
TerminalWidget.prototype.dispose = function () {
if (this.isDisposed) {
return;
}
this._session = null;
this._sheet = null;
this._term = null;
this._dummyTerm = null;
this._box = null;
_super.prototype.dispose.call(this);
};
/**
* Process a message sent to the widget.
*
* @param msg - The message sent to the widget.
*
* #### Notes
* Subclasses may reimplement this method as needed.
*/
TerminalWidget.prototype.processMessage = function (msg) {
_super.prototype.processMessage.call(this, msg);
switch (msg.type) {
case 'fit-request':
this.onFitRequest(msg);
break;
}
};
/**
* Set the size of the terminal when attached if dirty.
*/
TerminalWidget.prototype.onAfterAttach = function (msg) {
if (this._dirty) {
this._snapTermSizing();
}
};
/**
* Set the size of the terminal when shown if dirty.
*/
TerminalWidget.prototype.onAfterShow = function (msg) {
if (this._dirty) {
this._snapTermSizing();
}
this._term.focus();
};
/**
* Dispose of the terminal when closing.
*/
TerminalWidget.prototype.onCloseRequest = function (msg) {
_super.prototype.onCloseRequest.call(this, msg);
this.dispose();
};
/**
* On resize, use the computed row and column sizes to resize the terminal.
*/
TerminalWidget.prototype.onResize = function (msg) {
this._resizeTerminal(msg.width, msg.height);
};
/**
* A message handler invoked on an `'update-request'` message.
*/
TerminalWidget.prototype.onUpdateRequest = function (msg) {
// Set the fg and bg colors of the terminal and cursor.
this.node.style.backgroundColor = this.background;
this.node.style.color = this.color;
this._term.element.style.backgroundColor = this.background;
this._term.element.style.color = this.color;
this._sheet.innerHTML = ("#" + this.node.id + " .terminal-cursor {background:\n " + this.color + ";color:" + this.background + ";}");
};
/**
* A message handler invoked on an `'fit-request'` message.
*/
TerminalWidget.prototype.onFitRequest = function (msg) {
var resize = phosphor_widget_1.ResizeMessage.UnknownSize;
phosphor_messaging_1.sendMessage(this, resize);
};
/**
* Create the terminal object.
*/
TerminalWidget.prototype._initializeTerm = function () {
var _this = this;
this._term.open(this.node);
this._term.element.classList.add(TERMINAL_BODY_CLASS);
this._term.on('data', function (data) {
if (_this._session) {
_this._session.send({
type: 'stdin',
content: [data]
});
}
});
this._term.on('title', function (title) {
_this.title.text = title;
});
};
/**
* Handle a message from the terminal session.
*/
TerminalWidget.prototype._onMessage = function (sender, msg) {
switch (msg.type) {
case 'stdout':
this._term.write(msg.content[0]);
break;
case 'disconnect':
this._term.write('\r\n\r\n[Finished... Term Session]\r\n');
break;
}
};
/**
* Use the dummy terminal to measure the row and column sizes.
*/
TerminalWidget.prototype._snapTermSizing = function () {
if (!this.isVisible) {
this._dirty = true;
return;
}
var node = this._dummyTerm;
this._term.element.appendChild(node);
this._rowHeight = node.offsetHeight / DUMMY_ROWS;
this._colWidth = node.offsetWidth / DUMMY_COLS;
this._term.element.removeChild(node);
this._resizeTerminal(-1, -1);
};
/**
* Resize the terminal based on the computed geometry.
*
* The parent offset dimensions should be `-1` if unknown.
*/
TerminalWidget.prototype._resizeTerminal = function (offsetWidth, offsetHeight) {
if (this._rowHeight === -1 || !this.isVisible || !this._session) {
this._dirty = true;
return;
}
if (offsetWidth < 0) {
offsetWidth = this.node.offsetWidth;
}
if (offsetHeight < 0) {
offsetHeight = this.node.offsetHeight;
}
var box = this._box || (this._box = phosphor_domutil_1.boxSizing(this.node));
var height = offsetHeight - box.verticalSum;
var width = offsetWidth - box.horizontalSum;
var rows = Math.floor(height / this._rowHeight) - 1;
var cols = Math.floor(width / this._colWidth) - 1;
this._term.resize(cols, rows);
this._session.send({
type: 'set_size',
content: [rows, cols, height, width]
});
this._dirty = false;
this.update();
};
return TerminalWidget;
}(phosphor_widget_1.Widget));
exports.TerminalWidget = TerminalWidget;
/**
* A namespace for private data.
*/
var Private;
(function (Private) {
/**
* Get term.js options from ITerminalOptions.
*/
function getConfig(options) {
var config = {};
if (options.cursorBlink !== void 0) {
config.cursorBlink = options.cursorBlink;
}
else {
config.cursorBlink = true;
}
if (options.visualBell !== void 0) {
config.visualBell = options.visualBell;
}
if (options.popOnBell !== void 0) {
config.popOnBell = options.popOnBell;
}
if (options.scrollback !== void 0) {
config.scrollback = options.scrollback;
}
return config;
}
Private.getConfig = getConfig;
/**
* Create a dummy terminal element used to measure text size.
*/
function createDummyTerm() {
var node = document.createElement('div');
var rowspan = document.createElement('span');
rowspan.innerHTML = Array(DUMMY_ROWS).join('a<br>');
var colspan = document.createElement('span');
colspan.textContent = Array(DUMMY_COLS + 1).join('a');
node.appendChild(rowspan);
node.appendChild(colspan);
node.style.visibility = 'hidden';
node.style.position = 'absolute';
node.style.height = 'auto';
node.style.width = 'auto';
node.style['white-space'] = 'nowrap';
return node;
}
Private.createDummyTerm = createDummyTerm;
/**
* An incrementing counter for ids.
*/
Private.id = 0;
})(Private || (Private = {}));