mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into master
This commit is contained in:
Vendored
+6
-5
@@ -23,10 +23,7 @@
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Demo Client",
|
||||
"url": "http://0.0.0.0:3000",
|
||||
"windows": {
|
||||
"url": "http://127.0.0.1:3000"
|
||||
},
|
||||
"url": "http://127.0.0.1:3000",
|
||||
"webRoot": "${workspaceFolder}/"
|
||||
},
|
||||
{
|
||||
@@ -38,7 +35,11 @@
|
||||
"run",
|
||||
"start-debug"
|
||||
],
|
||||
"port": 9229
|
||||
"port": 9229,
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "App listening to (http://.*?:[0-9]+)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ Xterm.js is used in several world-class applications to provide great terminal e
|
||||
- [**Kubebox**](https://github.com/astefanutti/kubebox): Terminal console for Kubernetes clusters.
|
||||
- [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure.
|
||||
- [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace.
|
||||
- [**rtty**](https://github.com/zhaojh329/rtty): A reverse proxy WebTTY. It is composed of the client and the server.
|
||||
- [**rtty**](https://github.com/zhaojh329/rtty): Access your terminals from anywhere via the web.
|
||||
- [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS.
|
||||
- [**abstruse**](https://github.com/bleenco/abstruse): Abstruse CI is a continuous integration platform based on Node.JS and Docker.
|
||||
- [**Azure Data Studio**](https://github.com/Microsoft/azuredatastudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
|
||||
@@ -155,6 +155,8 @@ Xterm.js is used in several world-class applications to provide great terminal e
|
||||
- [**info-beamer hosted**](https://info-beamer.com): Uses xterm.js to manage digital signage devices from the web dashboard.
|
||||
- [**Jumpserver**](https://github.com/jumpserver/luna): Jumpserver Luna project, Jumpserver is a bastion server project, Luna use xterm.js for web terminal emulation.
|
||||
- [**LxdMosaic**](https://github.com/turtle0x1/LxdMosaic): Uses xterm.js to give terminal access to containers through LXD
|
||||
- [**CodeInterview.io**](https://codeinterview.io): A coding interview platform in 25+ languages and many web frameworks. Uses xterm.js to provide shell access.
|
||||
- [**Bastillion**](https://www.bastillion.io): Bastillion is an open-source web-based SSH console that centrally manages administrative access to systems.
|
||||
|
||||
[And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
|
||||
|
||||
|
||||
+7
-6
@@ -13,12 +13,11 @@ import * as fit from '../lib/addons/fit/fit';
|
||||
import * as fullscreen from '../lib/addons/fullscreen/fullscreen';
|
||||
import * as search from '../lib/addons/search/search';
|
||||
import * as webLinks from '../lib/addons/webLinks/webLinks';
|
||||
import * as winptyCompat from '../lib/addons/winptyCompat/winptyCompat';
|
||||
import { ISearchOptions } from '../lib/addons/search/Interfaces';
|
||||
|
||||
// Pulling in the module's types relies on the <reference> above, it's looks a
|
||||
// little weird here as we're importing "this" module
|
||||
import { Terminal as TerminalType } from 'xterm';
|
||||
import { Terminal as TerminalType, ITerminalOptions } from 'xterm';
|
||||
|
||||
export interface IWindowWithTerminal extends Window {
|
||||
term: TerminalType;
|
||||
@@ -30,7 +29,6 @@ Terminal.applyAddon(fit);
|
||||
Terminal.applyAddon(fullscreen);
|
||||
Terminal.applyAddon(search);
|
||||
Terminal.applyAddon(webLinks);
|
||||
Terminal.applyAddon(winptyCompat);
|
||||
|
||||
|
||||
let term;
|
||||
@@ -83,7 +81,10 @@ function createTerminal(): void {
|
||||
while (terminalContainer.children.length) {
|
||||
terminalContainer.removeChild(terminalContainer.children[0]);
|
||||
}
|
||||
term = new Terminal({});
|
||||
const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0;
|
||||
term = new Terminal({
|
||||
windowsMode: isWindows
|
||||
} as ITerminalOptions);
|
||||
window.term = term; // Expose `term` to window for debugging purposes
|
||||
term.on('resize', (size: { cols: number, rows: number }) => {
|
||||
if (!pid) {
|
||||
@@ -99,7 +100,7 @@ function createTerminal(): void {
|
||||
socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/';
|
||||
|
||||
term.open(terminalContainer);
|
||||
term.winptyCompatInit();
|
||||
|
||||
term.webLinksInit();
|
||||
term.fit();
|
||||
term.focus();
|
||||
@@ -171,7 +172,7 @@ function runFakeTerminal(): void {
|
||||
term.prompt();
|
||||
} else if (ev.keyCode === 8) {
|
||||
// Do not delete the prompt
|
||||
if (term.x > 2) {
|
||||
if (term._core.buffer.x > 2) {
|
||||
term.write('\b \b');
|
||||
}
|
||||
} else if (printable) {
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>xterm.js demo</title>
|
||||
<link rel="stylesheet" href="/build/xterm.css" />
|
||||
<link rel="stylesheet" href="/build/addons/fullscreen/fullscreen.css" />
|
||||
<link rel="stylesheet" href="/src/xterm.css" />
|
||||
<link rel="stylesheet" href="/src/addons/fullscreen/fullscreen.css" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/4.1.1/es6-promise.auto.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/1.0.0/fetch.min.js"></script>
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ function startServer() {
|
||||
var terminals = {},
|
||||
logs = {};
|
||||
|
||||
app.use('/build', express.static(__dirname + '/../build'));
|
||||
app.use('/src', express.static(__dirname + '/../src'));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.sendFile(__dirname + '/index.html');
|
||||
@@ -99,7 +99,7 @@ function startServer() {
|
||||
var port = process.env.PORT || 3000,
|
||||
host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0';
|
||||
|
||||
console.log('App listening to http://' + host + ':' + port);
|
||||
console.log('App listening to http://127.0.0.1:' + port);
|
||||
app.listen(port, host);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
var expressWs = require('express-ws')(app);
|
||||
var os = require('os');
|
||||
var pty = require('node-pty');
|
||||
|
||||
var terminals = {},
|
||||
logs = {};
|
||||
|
||||
app.use('/build', express.static(__dirname + '/../../build'));
|
||||
app.use('/demo', express.static(__dirname + '/../../demo'));
|
||||
app.use('/zmodemjs', express.static(__dirname + '/../../node_modules/zmodem.js/dist'));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.sendFile(__dirname + '/index.html');
|
||||
});
|
||||
|
||||
app.get('/style.css', function(req, res){
|
||||
res.sendFile(__dirname + '../style.css');
|
||||
});
|
||||
|
||||
app.get('/main.js', function(req, res){
|
||||
res.sendFile(__dirname + '/main.js');
|
||||
});
|
||||
|
||||
app.post('/terminals', function (req, res) {
|
||||
var cols = parseInt(req.query.cols),
|
||||
rows = parseInt(req.query.rows),
|
||||
term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], {
|
||||
encoding: null,
|
||||
name: 'xterm-color',
|
||||
cols: cols || 80,
|
||||
rows: rows || 24,
|
||||
cwd: process.env.PWD,
|
||||
env: process.env
|
||||
});
|
||||
|
||||
console.log('Created terminal with PID: ' + term.pid);
|
||||
terminals[term.pid] = term;
|
||||
logs[term.pid] = '';
|
||||
term.on('data', function(data) {
|
||||
logs[term.pid] += data;
|
||||
});
|
||||
res.send(term.pid.toString());
|
||||
res.end();
|
||||
});
|
||||
|
||||
app.post('/terminals/:pid/size', function (req, res) {
|
||||
var pid = parseInt(req.params.pid),
|
||||
cols = parseInt(req.query.cols),
|
||||
rows = parseInt(req.query.rows),
|
||||
term = terminals[pid];
|
||||
|
||||
term.resize(cols, rows);
|
||||
console.log('Resized terminal ' + pid + ' to ' + cols + ' cols and ' + rows + ' rows.');
|
||||
res.end();
|
||||
});
|
||||
|
||||
app.ws('/terminals/:pid', function (ws, req) {
|
||||
var term = terminals[parseInt(req.params.pid)];
|
||||
console.log('Connected to terminal ' + term.pid);
|
||||
ws.send(logs[term.pid]);
|
||||
|
||||
term.on('data', function(data) {
|
||||
try {
|
||||
ws.send(data);
|
||||
} catch (ex) {
|
||||
// The WebSocket is not open, ignore
|
||||
}
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
term.write(msg);
|
||||
});
|
||||
ws.on('close', function () {
|
||||
term.kill();
|
||||
console.log('Closed terminal ' + term.pid);
|
||||
// Clean things up
|
||||
delete terminals[term.pid];
|
||||
delete logs[term.pid];
|
||||
});
|
||||
});
|
||||
|
||||
var port = process.env.PORT || 3000,
|
||||
host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0';
|
||||
|
||||
console.log('App listening to http://' + host + ':' + port);
|
||||
app.listen(port, host);
|
||||
@@ -1,128 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>xterm.js demo</title>
|
||||
<link rel="stylesheet" href="/build/xterm.css" />
|
||||
<link rel="stylesheet" href="/build/addons/fullscreen/fullscreen.css" />
|
||||
<link rel="stylesheet" href="/demo/style.css" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/4.1.1/es6-promise.auto.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/1.0.0/fetch.min.js"></script>
|
||||
<script src="/build/xterm.js" ></script>
|
||||
|
||||
<script src="/build/addons/attach/attach.js" ></script>
|
||||
<script src="/zmodemjs/zmodem.js"></script>
|
||||
<script src="/build/addons/zmodem/zmodem.js" ></script>
|
||||
|
||||
<script src="/build/addons/fit/fit.js" ></script>
|
||||
<script src="/build/addons/fullscreen/fullscreen.js" ></script>
|
||||
<script src="/build/addons/search/search.js" ></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>xterm.js: xterm, in the browser</h1>
|
||||
|
||||
<div id="terminal-container"></div>
|
||||
|
||||
<div id="zmodem_controls">
|
||||
<form id="zm_start" style="display: none" action="javascript:void(0)">
|
||||
ZMODEM detected: Start ZMODEM session?
|
||||
<label><input id="zmstart_yes" name="zmstart" type=radio checked value="1"> Yes</label>
|
||||
|
||||
<label><input name="zmstart" type=radio value=""> No</label>
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form id="zm_offer" style="display: none" action="javascript:void(0)">
|
||||
<p>ZMODEM File offered!</p>
|
||||
|
||||
<label><input id="zmaccept_yes" name="zmaccept" type=radio checked value="1"> Accept</label>
|
||||
|
||||
<label><input name="zmaccept" type=radio value=""> Skip</label>
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<div id="zm_file" style="display: none">
|
||||
<div>Name: <span id="name"></span></div>
|
||||
<div>Size: <span id="size"></span></div>
|
||||
<div>Last modified: <span id="mtime"></span></div>
|
||||
<div>Mode: <span id="mode"></span></div>
|
||||
<br>
|
||||
<div>Conversion: <span id="zfile_conversion"></span></div>
|
||||
<div>Management: <span id="zfile_management"></span></div>
|
||||
<div>Transport: <span id="zfile_transport"></span></div>
|
||||
<div>Sparse? <span id="zfile_sparse"></span></div>
|
||||
<br>
|
||||
<div>Files remaining in batch: <span id="files_remaining"></span></div>
|
||||
<div>Bytes remaining in batch: <span id="bytes_remaining"></span></div>
|
||||
</div>
|
||||
|
||||
<form id="zm_progress" style="display: none" action="javascript:void(0)">
|
||||
<div><span id="percent_received"></span>% (<span id="bytes_received"></span> bytes) received</div>
|
||||
<button id="zm_progress_skipper" type="button" onclick="skip_current_file();">Skip File</button>
|
||||
</form>
|
||||
|
||||
<form id="zm_choose" style="display: none" action="javascript:void(0)">
|
||||
<label>Choose file(s): <input id="zm_files" type="file" multiple></label>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Actions</h2>
|
||||
<p>
|
||||
<label>Find next <input id="find-next"/></label>
|
||||
<label>Find previous <input id="find-previous"/></label>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Options</h2>
|
||||
<p>
|
||||
<label><input type="checkbox" id="option-cursor-blink"> cursorBlink</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><input type="checkbox" checked id="zmodem-auto"> Accept all ZMODEM prompts<sup>*</sup></label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
cursorStyle
|
||||
<select id="option-cursor-style">
|
||||
<option value="block">block</option>
|
||||
<option value="underline">underline</option>
|
||||
<option value="bar">bar</option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
bellStyle
|
||||
<select id="option-bell-style">
|
||||
<option value="">none</option>
|
||||
<option value="sound">sound</option>
|
||||
<option value="visual">visual</option>
|
||||
<option value="both">both</option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>scrollback <input type="number" id="option-scrollback" value="1000" /></label>
|
||||
</p>
|
||||
<p>
|
||||
<label>tabStopWidth <input type="number" id="option-tabstopwidth" value="8" /></label>
|
||||
</p>
|
||||
<div>
|
||||
<h3>Size</h3>
|
||||
<div>
|
||||
<div style="display: inline-block; margin-right: 16px;">
|
||||
<label for="cols">Columns</label>
|
||||
<input type="number" id="cols" />
|
||||
</div>
|
||||
<div style="display: inline-block; margin-right: 16px;">
|
||||
<label for="rows">Rows</label>
|
||||
<input type="number" id="rows" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p><strong>Attention:</strong> The demo is a barebones implementation and is designed for xterm.js evaluation purposes only. Exposing the demo to the public as is would introduce security risks for the host.</p>
|
||||
<p><sup>*</sup> ZMODEM file transfers are supported via an addon. To try it out, install <a href="https://ohse.de/uwe/software/lrzsz.html"><code>lrzsz</code></a> onto the remote peer, then run <code>rz</code> to send from your browser or <code>sz <file></code> to send from the remote peer.</p>
|
||||
<script src="main.js" defer ></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,388 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var term,
|
||||
protocol,
|
||||
socketURL,
|
||||
socket,
|
||||
pid;
|
||||
|
||||
Terminal.applyAddon(fit);
|
||||
Terminal.applyAddon(attach);
|
||||
Terminal.applyAddon(zmodem);
|
||||
Terminal.applyAddon(search);
|
||||
|
||||
var terminalContainer = document.getElementById('terminal-container'),
|
||||
actionElements = {
|
||||
findNext: document.querySelector('#find-next'),
|
||||
findPrevious: document.querySelector('#find-previous')
|
||||
},
|
||||
optionElements = {
|
||||
cursorBlink: document.querySelector('#option-cursor-blink'),
|
||||
cursorStyle: document.querySelector('#option-cursor-style'),
|
||||
scrollback: document.querySelector('#option-scrollback'),
|
||||
tabstopwidth: document.querySelector('#option-tabstopwidth'),
|
||||
bellStyle: document.querySelector('#option-bell-style')
|
||||
},
|
||||
colsElement = document.getElementById('cols'),
|
||||
rowsElement = document.getElementById('rows');
|
||||
|
||||
function setTerminalSize() {
|
||||
var cols = parseInt(colsElement.value, 10);
|
||||
var rows = parseInt(rowsElement.value, 10);
|
||||
var viewportElement = document.querySelector('.xterm-viewport');
|
||||
var scrollBarWidth = viewportElement.offsetWidth - viewportElement.clientWidth;
|
||||
var width = (cols * term.charMeasure.width + 20 /*room for scrollbar*/).toString() + 'px';
|
||||
var height = (rows * term.charMeasure.height).toString() + 'px';
|
||||
|
||||
terminalContainer.style.width = width;
|
||||
terminalContainer.style.height = height;
|
||||
term.resize(cols, rows);
|
||||
}
|
||||
|
||||
colsElement.addEventListener('change', setTerminalSize);
|
||||
rowsElement.addEventListener('change', setTerminalSize);
|
||||
|
||||
actionElements.findNext.addEventListener('keypress', function (e) {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
term.findNext(actionElements.findNext.value);
|
||||
}
|
||||
});
|
||||
actionElements.findPrevious.addEventListener('keypress', function (e) {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
term.findPrevious(actionElements.findPrevious.value);
|
||||
}
|
||||
});
|
||||
|
||||
optionElements.cursorBlink.addEventListener('change', function () {
|
||||
term.setOption('cursorBlink', optionElements.cursorBlink.checked);
|
||||
});
|
||||
optionElements.cursorStyle.addEventListener('change', function () {
|
||||
term.setOption('cursorStyle', optionElements.cursorStyle.value);
|
||||
});
|
||||
optionElements.bellStyle.addEventListener('change', function () {
|
||||
term.setOption('bellStyle', optionElements.bellStyle.value);
|
||||
});
|
||||
optionElements.scrollback.addEventListener('change', function () {
|
||||
term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10));
|
||||
});
|
||||
optionElements.tabstopwidth.addEventListener('change', function () {
|
||||
term.setOption('tabStopWidth', parseInt(optionElements.tabstopwidth.value, 10));
|
||||
});
|
||||
|
||||
createTerminal();
|
||||
|
||||
function createTerminal() {
|
||||
// Clean terminal
|
||||
while (terminalContainer.children.length) {
|
||||
terminalContainer.removeChild(terminalContainer.children[0]);
|
||||
}
|
||||
term = new Terminal({
|
||||
cursorBlink: optionElements.cursorBlink.checked,
|
||||
scrollback: parseInt(optionElements.scrollback.value, 10),
|
||||
tabStopWidth: parseInt(optionElements.tabstopwidth.value, 10)
|
||||
});
|
||||
term.on('resize', function (size) {
|
||||
if (!pid) {
|
||||
return;
|
||||
}
|
||||
var cols = size.cols,
|
||||
rows = size.rows,
|
||||
url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;
|
||||
|
||||
fetch(url, {method: 'POST'});
|
||||
});
|
||||
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
|
||||
socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/';
|
||||
|
||||
term.open(terminalContainer);
|
||||
term.fit();
|
||||
|
||||
// fit is called within a setTimeout, cols and rows need this.
|
||||
setTimeout(function () {
|
||||
colsElement.value = term.cols;
|
||||
rowsElement.value = term.rows;
|
||||
|
||||
// Set terminal size again to set the specific dimensions on the demo
|
||||
setTerminalSize();
|
||||
|
||||
fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) {
|
||||
|
||||
res.text().then(function (pid) {
|
||||
window.pid = pid;
|
||||
socketURL += pid;
|
||||
socket = new WebSocket(socketURL);
|
||||
socket.onopen = runRealTerminal;
|
||||
socket.onclose = runFakeTerminal;
|
||||
socket.onerror = runFakeTerminal;
|
||||
|
||||
term.zmodemAttach(socket, {
|
||||
noTerminalWriteOutsideSession: true,
|
||||
} );
|
||||
|
||||
term.on("zmodemRetract", () => {
|
||||
start_form.style.display = "none";
|
||||
start_form.onsubmit = null;
|
||||
});
|
||||
|
||||
term.on("zmodemDetect", (detection) => {
|
||||
function do_zmodem() {
|
||||
term.detach();
|
||||
let zsession = detection.confirm();
|
||||
|
||||
var promise;
|
||||
|
||||
if (zsession.type === "receive") {
|
||||
promise = _handle_receive_session(zsession);
|
||||
}
|
||||
else {
|
||||
promise = _handle_send_session(zsession);
|
||||
}
|
||||
|
||||
promise.catch( console.error.bind(console) ).then( () => {
|
||||
term.attach(socket);
|
||||
} );
|
||||
}
|
||||
|
||||
if (_auto_zmodem()) {
|
||||
do_zmodem();
|
||||
}
|
||||
else {
|
||||
start_form.style.display = "";
|
||||
start_form.onsubmit = function(e) {
|
||||
start_form.style.display = "none";
|
||||
|
||||
if (document.getElementById("zmstart_yes").checked) {
|
||||
do_zmodem();
|
||||
}
|
||||
else {
|
||||
detection.deny();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// UI STUFF
|
||||
|
||||
function _show_file_info(xfer) {
|
||||
var file_info = xfer.get_details();
|
||||
|
||||
document.getElementById("name").textContent = file_info.name;
|
||||
document.getElementById("size").textContent = file_info.size;
|
||||
document.getElementById("mtime").textContent = file_info.mtime;
|
||||
document.getElementById("files_remaining").textContent = file_info.files_remaining;
|
||||
document.getElementById("bytes_remaining").textContent = file_info.bytes_remaining;
|
||||
|
||||
document.getElementById("mode").textContent = "0" + file_info.mode.toString(8);
|
||||
|
||||
var xfer_opts = xfer.get_options();
|
||||
["conversion", "management", "transport", "sparse"].forEach( (lbl) => {
|
||||
document.getElementById(`zfile_${lbl}`).textContent = xfer_opts[lbl];
|
||||
} );
|
||||
|
||||
document.getElementById("zm_file").style.display = "";
|
||||
}
|
||||
function _hide_file_info() {
|
||||
document.getElementById("zm_file").style.display = "none";
|
||||
}
|
||||
|
||||
function _save_to_disk(xfer, buffer) {
|
||||
return Zmodem.Browser.save_to_disk(buffer, xfer.get_details().name);
|
||||
}
|
||||
|
||||
var skipper_button = document.getElementById("zm_progress_skipper");
|
||||
var skipper_button_orig_text = skipper_button.textContent;
|
||||
|
||||
function _show_progress() {
|
||||
skipper_button.disabled = false;
|
||||
skipper_button.textContent = skipper_button_orig_text;
|
||||
|
||||
document.getElementById("bytes_received").textContent = 0;
|
||||
document.getElementById("percent_received").textContent = 0;
|
||||
|
||||
document.getElementById("zm_progress").style.display = "";
|
||||
}
|
||||
|
||||
function _update_progress(xfer) {
|
||||
var total_in = xfer.get_offset();
|
||||
|
||||
document.getElementById("bytes_received").textContent = total_in;
|
||||
|
||||
var percent_received = 100 * total_in / xfer.get_details().size;
|
||||
document.getElementById("percent_received").textContent = percent_received.toFixed(2);
|
||||
}
|
||||
|
||||
function _hide_progress() {
|
||||
document.getElementById("zm_progress").style.display = "none";
|
||||
}
|
||||
|
||||
var start_form = document.getElementById("zm_start");
|
||||
|
||||
function _auto_zmodem() {
|
||||
return document.getElementById("zmodem-auto").checked;
|
||||
}
|
||||
|
||||
// END UI STUFF
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
function _handle_receive_session(zsession) {
|
||||
zsession.on("offer", function(xfer) {
|
||||
current_receive_xfer = xfer;
|
||||
|
||||
_show_file_info(xfer);
|
||||
|
||||
var offer_form = document.getElementById("zm_offer");
|
||||
|
||||
function on_form_submit() {
|
||||
offer_form.style.display = "none";
|
||||
|
||||
//START
|
||||
//if (offer_form.zmaccept.value) {
|
||||
if (_auto_zmodem() || document.getElementById("zmaccept_yes").checked) {
|
||||
_show_progress();
|
||||
|
||||
var FILE_BUFFER = [];
|
||||
xfer.on("input", (payload) => {
|
||||
_update_progress(xfer);
|
||||
FILE_BUFFER.push( new Uint8Array(payload) );
|
||||
});
|
||||
xfer.accept().then(
|
||||
() => {
|
||||
_save_to_disk(xfer, FILE_BUFFER);
|
||||
},
|
||||
console.error.bind(console)
|
||||
);
|
||||
}
|
||||
else {
|
||||
xfer.skip();
|
||||
}
|
||||
//END
|
||||
}
|
||||
|
||||
if (_auto_zmodem()) {
|
||||
on_form_submit();
|
||||
}
|
||||
else {
|
||||
offer_form.onsubmit = on_form_submit;
|
||||
offer_form.style.display = "";
|
||||
}
|
||||
} );
|
||||
|
||||
var promise = new Promise( (res) => {
|
||||
zsession.on("session_end", () => {
|
||||
_hide_file_info();
|
||||
_hide_progress();
|
||||
res();
|
||||
} );
|
||||
} );
|
||||
|
||||
zsession.start();
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function _handle_send_session(zsession) {
|
||||
var choose_form = document.getElementById("zm_choose");
|
||||
choose_form.style.display = "";
|
||||
|
||||
var file_el = document.getElementById("zm_files");
|
||||
|
||||
var promise = new Promise( (res) => {
|
||||
file_el.onchange = function(e) {
|
||||
choose_form.style.display = "none";
|
||||
|
||||
var files_obj = file_el.files;
|
||||
|
||||
Zmodem.Browser.send_files(
|
||||
zsession,
|
||||
files_obj,
|
||||
{
|
||||
on_offer_response(obj, xfer) {
|
||||
if (xfer) _show_progress();
|
||||
//console.log("offer", xfer ? "accepted" : "skipped");
|
||||
},
|
||||
on_progress(obj, xfer) {
|
||||
_update_progress(xfer);
|
||||
},
|
||||
on_file_complete(obj) {
|
||||
//console.log("COMPLETE", obj);
|
||||
_hide_progress();
|
||||
},
|
||||
}
|
||||
).then(_hide_progress).then(
|
||||
zsession.close.bind(zsession),
|
||||
console.error.bind(console)
|
||||
).then( () => {
|
||||
_hide_file_info();
|
||||
_hide_progress();
|
||||
res();
|
||||
} );
|
||||
};
|
||||
} );
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
//This is here to allow canceling of an in-progress ZMODEM transfer.
|
||||
var current_receive_xfer;
|
||||
|
||||
//Called from HTML directly.
|
||||
function skip_current_file() {
|
||||
current_receive_xfer.skip();
|
||||
|
||||
skipper_button.disabled = true;
|
||||
skipper_button.textContent = "Waiting for server to acknowledge skip …";
|
||||
}
|
||||
|
||||
function runRealTerminal() {
|
||||
term.attach(socket);
|
||||
|
||||
term._initialized = true;
|
||||
}
|
||||
|
||||
function runFakeTerminal() {
|
||||
if (term._initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
term._initialized = true;
|
||||
|
||||
var shellprompt = '$ ';
|
||||
|
||||
term.prompt = function () {
|
||||
term.write('\r\n' + shellprompt);
|
||||
};
|
||||
|
||||
term.writeln('Welcome to xterm.js');
|
||||
term.writeln('This is a local terminal emulation, without a real terminal in the back-end.');
|
||||
term.writeln('Type some keys and commands to play around.');
|
||||
term.writeln('');
|
||||
term.prompt();
|
||||
|
||||
term.on('key', function (key, ev) {
|
||||
var printable = (
|
||||
!ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey
|
||||
);
|
||||
|
||||
if (ev.keyCode == 13) {
|
||||
term.prompt();
|
||||
} else if (ev.keyCode == 8) {
|
||||
// Do not delete the prompt
|
||||
if (term.x > 2) {
|
||||
term.write('\b \b');
|
||||
}
|
||||
} else if (printable) {
|
||||
term.write(key);
|
||||
}
|
||||
});
|
||||
|
||||
term.on('paste', function (data, ev) {
|
||||
term.write(data);
|
||||
});
|
||||
}
|
||||
+5
-5
@@ -16,9 +16,9 @@ const ts = require('gulp-typescript');
|
||||
const util = require('gulp-util');
|
||||
|
||||
const buildDir = process.env.BUILD_DIR || 'build';
|
||||
const tsProject = ts.createProject('tsconfig.json');
|
||||
let srcDir = tsProject.config.compilerOptions.rootDir;
|
||||
let outDir = tsProject.config.compilerOptions.outDir;
|
||||
const tsProject = ts.createProject('src/tsconfig.json');
|
||||
let srcDir = './src';
|
||||
let outDir = './lib';
|
||||
|
||||
const addons = fs.readdirSync(`${__dirname}/src/addons`);
|
||||
|
||||
@@ -61,7 +61,7 @@ gulp.task('browserify', function() {
|
||||
};
|
||||
let bundleStream = browserify(browserifyOptions)
|
||||
.bundle()
|
||||
.pipe(source('xterm.js'))
|
||||
.pipe(source(`xterm.js`))
|
||||
.pipe(buffer())
|
||||
.pipe(sourcemaps.init({loadMaps: true, sourceRoot: '..'}))
|
||||
.pipe(sourcemaps.write('./'))
|
||||
@@ -136,6 +136,6 @@ gulp.task('sorcery-addons', ['browserify-addons'], function () {
|
||||
})
|
||||
});
|
||||
|
||||
gulp.task('build', ['sorcery', 'sorcery-addons']);
|
||||
gulp.task('build', ['css', 'sorcery', 'sorcery-addons']);
|
||||
gulp.task('test', ['mocha']);
|
||||
gulp.task('default', ['build']);
|
||||
|
||||
+3
-8
@@ -15,7 +15,6 @@
|
||||
"@types/webpack": "^4.4.11",
|
||||
"browserify": "^13.3.0",
|
||||
"chai": "3.5.0",
|
||||
"concurrently": "^3.5.1",
|
||||
"coveralls": "^3.0.1",
|
||||
"express": "4.13.4",
|
||||
"express-ws": "2.0.0-rc.1",
|
||||
@@ -38,7 +37,7 @@
|
||||
"ts-loader": "^4.5.0",
|
||||
"tslint": "^5.9.1",
|
||||
"tslint-consistent-codestyle": "^1.13.0",
|
||||
"typescript": "3.1",
|
||||
"typescript": "3.4",
|
||||
"vinyl-buffer": "^1.0.0",
|
||||
"vinyl-source-stream": "^1.1.0",
|
||||
"webpack": "^4.17.1",
|
||||
@@ -50,20 +49,16 @@
|
||||
"start-debug": "node --inspect-brk demo/start",
|
||||
"start-zmodem": "node demo/zmodem/app",
|
||||
"lint": "tslint 'src/**/*.ts' './demo/**/*.ts'",
|
||||
"pretest": "npm run layering",
|
||||
"test": "npm run mocha",
|
||||
"posttest": "npm run lint",
|
||||
"test-debug": "node --inspect-brk node_modules/.bin/gulp test",
|
||||
"test-suite": "gulp mocha-suite --test",
|
||||
"test-coverage": "nyc -x gulpfile.js -x '**/*test*' npm run mocha",
|
||||
"mocha": "gulp test",
|
||||
"tsc": "tsc",
|
||||
"prebuild": "concurrently --kill-others-on-fail --names \"lib,attach,fit,fullscreen,search,terminado,webLinks,winptyCompat,zmodem,css\" \"tsc\" \"tsc -p ./src/addons/attach\" \"tsc -p ./src/addons/fit\" \"tsc -p ./src/addons/fullscreen\" \"tsc -p ./src/addons/search\" \"tsc -p ./src/addons/terminado\" \"tsc -p ./src/addons/webLinks\" \"tsc -p ./src/addons/winptyCompat\" \"tsc -p ./src/addons/zmodem\" \"gulp css\"",
|
||||
"prebuild": "tsc -b ./src/tsconfig.all.json",
|
||||
"build": "gulp build",
|
||||
"prepublish": "npm run build",
|
||||
"coveralls": "nyc report --reporter=text-lcov | coveralls",
|
||||
"watch": "concurrently --kill-others-on-fail --names \"lib,css\" \"tsc -w\" \"gulp watch-css\"",
|
||||
"watch-addons": "concurrently --kill-others-on-fail --names \"attach,fit,fullscreen,search,terminado,webLinks,winptyCompat,zmodem\" \"tsc -w -p ./src/addons/attach\" \"tsc -w -p ./src/addons/fit\" \"tsc -w -p ./src/addons/fullscreen\" \"tsc -w -p ./src/addons/search\" \"tsc -w -p ./src/addons/terminado\" \"tsc -w -p ./src/addons/webLinks\" \"tsc -w -p ./src/addons/winptyCompat\" \"tsc -w -p ./src/addons/zmodem\"",
|
||||
"layering": "concurrently --kill-others-on-fail --names \"common,core\" \"tsc -p ./src/common\" \"tsc -p ./src/core\""
|
||||
"watch": "tsc -b -w ./src/tsconfig.all.json --preserveWatchOutput"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as Strings from './Strings';
|
||||
import { ITerminal, IBuffer } from './Types';
|
||||
import { isMac } from './core/Platform';
|
||||
import { isMac } from './common/Platform';
|
||||
import { RenderDebouncer } from './ui/RenderDebouncer';
|
||||
import { addDisposableDomListener } from './ui/Lifecycle';
|
||||
import { Disposable } from './common/Lifecycle';
|
||||
|
||||
+85
-36
@@ -5,10 +5,10 @@
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { ITerminal } from './Types';
|
||||
import { Buffer, DEFAULT_ATTR, CHAR_DATA_CHAR_INDEX } from './Buffer';
|
||||
import { Buffer, DEFAULT_ATTR_DATA } from './Buffer';
|
||||
import { CircularList } from './common/CircularList';
|
||||
import { MockTerminal, TestTerminal } from './ui/TestUtils.test';
|
||||
import { BufferLine } from './BufferLine';
|
||||
import { BufferLine, CellData } from './BufferLine';
|
||||
|
||||
const INIT_COLS = 80;
|
||||
const INIT_ROWS = 24;
|
||||
@@ -37,13 +37,13 @@ describe('Buffer', () => {
|
||||
|
||||
describe('fillViewportRows', () => {
|
||||
it('should fill the buffer with blank lines based on the size of the viewport', () => {
|
||||
const blankLineChar = buffer.getBlankLine(DEFAULT_ATTR).get(0);
|
||||
const blankLineChar = buffer.getBlankLine(DEFAULT_ATTR_DATA).loadCell(0, new CellData()).getAsCharData();
|
||||
buffer.fillViewportRows();
|
||||
assert.equal(buffer.lines.length, INIT_ROWS);
|
||||
for (let y = 0; y < INIT_ROWS; y++) {
|
||||
assert.equal(buffer.lines.get(y).length, INIT_COLS);
|
||||
for (let x = 0; x < INIT_COLS; x++) {
|
||||
assert.deepEqual(buffer.lines.get(y).get(x), blankLineChar);
|
||||
assert.deepEqual(buffer.lines.get(y).loadCell(x, new CellData()).getAsCharData(), blankLineChar);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -155,15 +155,15 @@ describe('Buffer', () => {
|
||||
assert.equal(buffer.lines.maxLength, INIT_ROWS);
|
||||
buffer.y = INIT_ROWS - 1;
|
||||
buffer.fillViewportRows();
|
||||
let chData = buffer.lines.get(5).get(0);
|
||||
let chData = buffer.lines.get(5).loadCell(0, new CellData()).getAsCharData();
|
||||
chData[1] = 'a';
|
||||
buffer.lines.get(5).set(0, chData);
|
||||
chData = buffer.lines.get(INIT_ROWS - 1).get(0);
|
||||
buffer.lines.get(5).setCell(0, CellData.fromCharData(chData));
|
||||
chData = buffer.lines.get(INIT_ROWS - 1).loadCell(0, new CellData()).getAsCharData();
|
||||
chData[1] = 'b';
|
||||
buffer.lines.get(INIT_ROWS - 1).set(0, chData);
|
||||
buffer.lines.get(INIT_ROWS - 1).setCell(0, CellData.fromCharData(chData));
|
||||
buffer.resize(INIT_COLS, INIT_ROWS - 5);
|
||||
assert.equal(buffer.lines.get(0).get(0)[1], 'a');
|
||||
assert.equal(buffer.lines.get(INIT_ROWS - 1 - 5).get(0)[1], 'b');
|
||||
assert.equal(buffer.lines.get(0).loadCell(0, new CellData()).getAsCharData()[1], 'a');
|
||||
assert.equal(buffer.lines.get(INIT_ROWS - 1 - 5).loadCell(0, new CellData()).getAsCharData()[1], 'b');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -184,7 +184,7 @@ describe('Buffer', () => {
|
||||
buffer.fillViewportRows();
|
||||
// Create 10 extra blank lines
|
||||
for (let i = 0; i < 10; i++) {
|
||||
buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR));
|
||||
buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||
}
|
||||
// Set cursor to the bottom of the buffer
|
||||
buffer.y = INIT_ROWS - 1;
|
||||
@@ -204,7 +204,7 @@ describe('Buffer', () => {
|
||||
buffer.fillViewportRows();
|
||||
// Create 10 extra blank lines
|
||||
for (let i = 0; i < 10; i++) {
|
||||
buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR));
|
||||
buffer.lines.push(buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||
}
|
||||
// Set cursor to the bottom of the buffer
|
||||
buffer.y = INIT_ROWS - 1;
|
||||
@@ -518,6 +518,29 @@ describe('Buffer', () => {
|
||||
assert.equal(secondMarker.line, 1, 'second marker should be restored');
|
||||
assert.equal(thirdMarker.line, 2, 'third marker should be restored');
|
||||
});
|
||||
it('should correctly reflow wrapped lines that end in null space (via tab char)', () => {
|
||||
buffer.fillViewportRows();
|
||||
buffer.resize(4, 10);
|
||||
buffer.y = 2;
|
||||
buffer.lines.get(0).set(0, [null, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
buffer.lines.get(0).set(1, [null, 'b', 1, 'b'.charCodeAt(0)]);
|
||||
buffer.lines.get(1).set(0, [null, 'c', 1, 'c'.charCodeAt(0)]);
|
||||
buffer.lines.get(1).set(1, [null, 'd', 1, 'd'.charCodeAt(0)]);
|
||||
buffer.lines.get(1).isWrapped = true;
|
||||
// Buffer:
|
||||
// "ab " (wrapped)
|
||||
// "cd"
|
||||
buffer.resize(5, 10);
|
||||
assert.equal(buffer.ybase, 0);
|
||||
assert.equal(buffer.lines.length, 10);
|
||||
assert.equal(buffer.lines.get(0).translateToString(true), 'ab c');
|
||||
assert.equal(buffer.lines.get(1).translateToString(false), 'd ');
|
||||
buffer.resize(6, 10);
|
||||
assert.equal(buffer.ybase, 0);
|
||||
assert.equal(buffer.lines.length, 10);
|
||||
assert.equal(buffer.lines.get(0).translateToString(true), 'ab cd');
|
||||
assert.equal(buffer.lines.get(1).translateToString(false), ' ');
|
||||
});
|
||||
it('should wrap wide characters correctly when reflowing larger', () => {
|
||||
buffer.fillViewportRows();
|
||||
buffer.resize(12, 10);
|
||||
@@ -553,6 +576,32 @@ describe('Buffer', () => {
|
||||
assert.equal(buffer.lines.get(1).translateToString(true), '语汉语汉语');
|
||||
assert.equal(buffer.lines.get(1).translateToString(false), '语汉语汉语 ');
|
||||
});
|
||||
it('should correctly reflow wrapped lines that end in null space (via tab char)', () => {
|
||||
buffer.fillViewportRows();
|
||||
buffer.resize(4, 10);
|
||||
buffer.y = 2;
|
||||
buffer.lines.get(0).set(0, [null, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
buffer.lines.get(0).set(1, [null, 'b', 1, 'b'.charCodeAt(0)]);
|
||||
buffer.lines.get(1).set(0, [null, 'c', 1, 'c'.charCodeAt(0)]);
|
||||
buffer.lines.get(1).set(1, [null, 'd', 1, 'd'.charCodeAt(0)]);
|
||||
buffer.lines.get(1).isWrapped = true;
|
||||
// Buffer:
|
||||
// "ab " (wrapped)
|
||||
// "cd"
|
||||
buffer.resize(3, 10);
|
||||
assert.equal(buffer.y, 2);
|
||||
assert.equal(buffer.ybase, 0);
|
||||
assert.equal(buffer.lines.length, 10);
|
||||
assert.equal(buffer.lines.get(0).translateToString(false), 'ab ');
|
||||
assert.equal(buffer.lines.get(1).translateToString(false), ' cd');
|
||||
buffer.resize(2, 10);
|
||||
assert.equal(buffer.y, 3);
|
||||
assert.equal(buffer.ybase, 0);
|
||||
assert.equal(buffer.lines.length, 10);
|
||||
assert.equal(buffer.lines.get(0).translateToString(false), 'ab');
|
||||
assert.equal(buffer.lines.get(1).translateToString(false), ' ');
|
||||
assert.equal(buffer.lines.get(2).translateToString(false), 'cd');
|
||||
});
|
||||
it('should wrap wide characters correctly when reflowing smaller', () => {
|
||||
buffer.fillViewportRows();
|
||||
buffer.resize(12, 10);
|
||||
@@ -683,7 +732,7 @@ describe('Buffer', () => {
|
||||
beforeEach(() => {
|
||||
// Add 10 empty rows to start
|
||||
for (let i = 0; i < 10; i++) {
|
||||
buffer.lines.splice(0, 0, buffer.getBlankLine(DEFAULT_ATTR));
|
||||
buffer.lines.splice(0, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||
}
|
||||
buffer.ybase = 10;
|
||||
});
|
||||
@@ -742,7 +791,7 @@ describe('Buffer', () => {
|
||||
terminal.options.scrollback = 10;
|
||||
// Add 10 empty rows to start
|
||||
for (let i = 0; i < 10; i++) {
|
||||
buffer.lines.splice(0, 0, buffer.getBlankLine(DEFAULT_ATTR));
|
||||
buffer.lines.splice(0, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||
}
|
||||
buffer.y = 9;
|
||||
buffer.ybase = 10;
|
||||
@@ -877,7 +926,7 @@ describe('Buffer', () => {
|
||||
beforeEach(() => {
|
||||
// Add 10 empty rows to start
|
||||
for (let i = 0; i < 10; i++) {
|
||||
buffer.lines.splice(0, 0, buffer.getBlankLine(DEFAULT_ATTR));
|
||||
buffer.lines.splice(0, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||
}
|
||||
buffer.ybase = 10;
|
||||
});
|
||||
@@ -940,7 +989,7 @@ describe('Buffer', () => {
|
||||
terminal.options.scrollback = 10;
|
||||
// Add 10 empty rows to start
|
||||
for (let i = 0; i < 10; i++) {
|
||||
buffer.lines.splice(0, 0, buffer.getBlankLine(DEFAULT_ATTR));
|
||||
buffer.lines.splice(0, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||
}
|
||||
buffer.ybase = 10;
|
||||
});
|
||||
@@ -1045,10 +1094,10 @@ describe('Buffer', () => {
|
||||
describe ('translateBufferLineToString', () => {
|
||||
it('should handle selecting a section of ascii text', () => {
|
||||
const line = new BufferLine(4);
|
||||
line.set(0, [ null, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(1, [ null, 'b', 1, 'b'.charCodeAt(0)]);
|
||||
line.set(2, [ null, 'c', 1, 'c'.charCodeAt(0)]);
|
||||
line.set(3, [ null, 'd', 1, 'd'.charCodeAt(0)]);
|
||||
line.setCell(0, CellData.fromCharData([ null, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(1, CellData.fromCharData([ null, 'b', 1, 'b'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([ null, 'c', 1, 'c'.charCodeAt(0)]));
|
||||
line.setCell(3, CellData.fromCharData([ null, 'd', 1, 'd'.charCodeAt(0)]));
|
||||
buffer.lines.set(0, line);
|
||||
|
||||
const str = buffer.translateBufferLineToString(0, true, 0, 2);
|
||||
@@ -1057,9 +1106,9 @@ describe('Buffer', () => {
|
||||
|
||||
it('should handle a cut-off double width character by including it', () => {
|
||||
const line = new BufferLine(3);
|
||||
line.set(0, [ null, '語', 2, 35486 ]);
|
||||
line.set(1, [ null, '', 0, null]);
|
||||
line.set(2, [ null, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.setCell(0, CellData.fromCharData([ null, '語', 2, 35486 ]));
|
||||
line.setCell(1, CellData.fromCharData([ null, '', 0, null]));
|
||||
line.setCell(2, CellData.fromCharData([ null, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
buffer.lines.set(0, line);
|
||||
|
||||
const str1 = buffer.translateBufferLineToString(0, true, 0, 1);
|
||||
@@ -1068,9 +1117,9 @@ describe('Buffer', () => {
|
||||
|
||||
it('should handle a zero width character in the middle of the string by not including it', () => {
|
||||
const line = new BufferLine(3);
|
||||
line.set(0, [ null, '語', 2, '語'.charCodeAt(0) ]);
|
||||
line.set(1, [ null, '', 0, null]);
|
||||
line.set(2, [ null, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.setCell(0, CellData.fromCharData([ null, '語', 2, '語'.charCodeAt(0) ]));
|
||||
line.setCell(1, CellData.fromCharData([ null, '', 0, null]));
|
||||
line.setCell(2, CellData.fromCharData([ null, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
buffer.lines.set(0, line);
|
||||
|
||||
const str0 = buffer.translateBufferLineToString(0, true, 0, 1);
|
||||
@@ -1085,8 +1134,8 @@ describe('Buffer', () => {
|
||||
|
||||
it('should handle single width emojis', () => {
|
||||
const line = new BufferLine(2);
|
||||
line.set(0, [ null, '😁', 1, '😁'.charCodeAt(0) ]);
|
||||
line.set(1, [ null, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.setCell(0, CellData.fromCharData([ null, '😁', 1, '😁'.charCodeAt(0) ]));
|
||||
line.setCell(1, CellData.fromCharData([ null, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
buffer.lines.set(0, line);
|
||||
|
||||
const str1 = buffer.translateBufferLineToString(0, true, 0, 1);
|
||||
@@ -1098,8 +1147,8 @@ describe('Buffer', () => {
|
||||
|
||||
it('should handle double width emojis', () => {
|
||||
const line = new BufferLine(2);
|
||||
line.set(0, [ null, '😁', 2, '😁'.charCodeAt(0) ]);
|
||||
line.set(1, [ null, '', 0, null]);
|
||||
line.setCell(0, CellData.fromCharData([ null, '😁', 2, '😁'.charCodeAt(0) ]));
|
||||
line.setCell(1, CellData.fromCharData([ null, '', 0, null]));
|
||||
buffer.lines.set(0, line);
|
||||
|
||||
const str1 = buffer.translateBufferLineToString(0, true, 0, 1);
|
||||
@@ -1109,9 +1158,9 @@ describe('Buffer', () => {
|
||||
assert.equal(str2, '😁');
|
||||
|
||||
const line2 = new BufferLine(3);
|
||||
line2.set(0, [ null, '😁', 2, '😁'.charCodeAt(0) ]);
|
||||
line2.set(1, [ null, '', 0, null]);
|
||||
line2.set(2, [ null, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line2.setCell(0, CellData.fromCharData([ null, '😁', 2, '😁'.charCodeAt(0) ]));
|
||||
line2.setCell(1, CellData.fromCharData([ null, '', 0, null]));
|
||||
line2.setCell(2, CellData.fromCharData([ null, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
buffer.lines.set(0, line2);
|
||||
|
||||
const str3 = buffer.translateBufferLineToString(0, true, 0, 3);
|
||||
@@ -1264,7 +1313,7 @@ describe('Buffer', () => {
|
||||
assert.equal(input, s);
|
||||
const stringIndex = s.match(/😃/).index;
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, stringIndex);
|
||||
assert(terminal.buffer.lines.get(bufferIndex[0]).get(bufferIndex[1])[CHAR_DATA_CHAR_INDEX], '😃');
|
||||
assert(terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars(), '😃');
|
||||
});
|
||||
|
||||
it('multiline fullwidth chars with offset 1 (currently tests for broken behavior)', () => {
|
||||
@@ -1291,7 +1340,7 @@ describe('Buffer', () => {
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||
assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0]).get(bufferIndex[1])[CHAR_DATA_CHAR_INDEX]);
|
||||
assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1309,7 +1358,7 @@ describe('Buffer', () => {
|
||||
: (i % 3 === 1)
|
||||
? input.substr(i, 2)
|
||||
: input.substr(i - 1, 2),
|
||||
terminal.buffer.lines.get(bufferIndex[0]).get(bufferIndex[1])[CHAR_DATA_CHAR_INDEX]);
|
||||
terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+63
-23
@@ -3,31 +3,43 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IMarker } from 'xterm';
|
||||
import { BufferLine } from './BufferLine';
|
||||
import { reflowLargerApplyNewLayout, reflowLargerCreateNewLayout, reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths } from './BufferReflow';
|
||||
import { CircularList, IDeleteEvent, IInsertEvent } from './common/CircularList';
|
||||
import { CircularList, IInsertEvent, IDeleteEvent } from './common/CircularList';
|
||||
import { ITerminal, IBuffer, IBufferLine, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult, ICellData, IAttributeData } from './Types';
|
||||
import { EventEmitter } from './common/EventEmitter';
|
||||
import { IMarker } from 'xterm';
|
||||
import { BufferLine, CellData, AttributeData } from './BufferLine';
|
||||
import { reflowLargerApplyNewLayout, reflowLargerCreateNewLayout, reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths, getWrappedLineTrimmedLength } from './BufferReflow';
|
||||
import { DEFAULT_COLOR } from './renderer/atlas/Types';
|
||||
import { BufferIndex, CharData, IBuffer, IBufferLine, IBufferStringIterator, IBufferStringIteratorResult, ITerminal } from './Types';
|
||||
|
||||
|
||||
export const DEFAULT_ATTR = (0 << 18) | (DEFAULT_COLOR << 9) | (256 << 0);
|
||||
|
||||
export const DEFAULT_ATTR_DATA = new AttributeData();
|
||||
|
||||
export const CHAR_DATA_ATTR_INDEX = 0;
|
||||
export const CHAR_DATA_CHAR_INDEX = 1;
|
||||
export const CHAR_DATA_WIDTH_INDEX = 2;
|
||||
export const CHAR_DATA_CODE_INDEX = 3;
|
||||
export const MAX_BUFFER_SIZE = 4294967295; // 2^32 - 1
|
||||
|
||||
/**
|
||||
* Null cell - a real empty cell (containing nothing).
|
||||
* Note that code should always be 0 for a null cell as
|
||||
* several test condition of the buffer line rely on this.
|
||||
*/
|
||||
export const NULL_CELL_CHAR = '';
|
||||
export const NULL_CELL_WIDTH = 1;
|
||||
export const NULL_CELL_CODE = 0;
|
||||
|
||||
/**
|
||||
* Whitespace cell.
|
||||
* This is meant as a replacement for empty cells when needed
|
||||
* during rendering lines to preserve correct aligment.
|
||||
*/
|
||||
export const WHITESPACE_CELL_CHAR = ' ';
|
||||
export const WHITESPACE_CELL_WIDTH = 1;
|
||||
export const WHITESPACE_CELL_CODE = 32;
|
||||
|
||||
export const FILL_CHAR_DATA: CharData = [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE];
|
||||
|
||||
/**
|
||||
* This class represents a terminal buffer (an internal state of the terminal), where the
|
||||
* following information is stored (in high-level):
|
||||
@@ -46,8 +58,10 @@ export class Buffer implements IBuffer {
|
||||
public tabs: any;
|
||||
public savedY: number;
|
||||
public savedX: number;
|
||||
public savedCurAttr: number;
|
||||
public savedCurAttrData = DEFAULT_ATTR_DATA.clone();
|
||||
public markers: Marker[] = [];
|
||||
private _nullCell: ICellData = CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
private _whitespaceCell: ICellData = CellData.fromCharData([0, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE]);
|
||||
private _cols: number;
|
||||
private _rows: number;
|
||||
|
||||
@@ -66,9 +80,30 @@ export class Buffer implements IBuffer {
|
||||
this.clear();
|
||||
}
|
||||
|
||||
public getBlankLine(attr: number, isWrapped?: boolean): IBufferLine {
|
||||
const fillCharData: CharData = [attr, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE];
|
||||
return new BufferLine(this._cols, fillCharData, isWrapped);
|
||||
public getNullCell(attr?: IAttributeData): ICellData {
|
||||
if (attr) {
|
||||
this._nullCell.fg = attr.fg;
|
||||
this._nullCell.bg = attr.bg;
|
||||
} else {
|
||||
this._nullCell.fg = 0;
|
||||
this._nullCell.bg = 0;
|
||||
}
|
||||
return this._nullCell;
|
||||
}
|
||||
|
||||
public getWhitespaceCell(attr?: IAttributeData): ICellData {
|
||||
if (attr) {
|
||||
this._whitespaceCell.fg = attr.fg;
|
||||
this._whitespaceCell.bg = attr.bg;
|
||||
} else {
|
||||
this._whitespaceCell.fg = 0;
|
||||
this._whitespaceCell.bg = 0;
|
||||
}
|
||||
return this._whitespaceCell;
|
||||
}
|
||||
|
||||
public getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine {
|
||||
return new BufferLine(this._terminal.cols, this.getNullCell(attr), isWrapped);
|
||||
}
|
||||
|
||||
public get hasScrollback(): boolean {
|
||||
@@ -99,10 +134,10 @@ export class Buffer implements IBuffer {
|
||||
/**
|
||||
* Fills the buffer's viewport with blank lines.
|
||||
*/
|
||||
public fillViewportRows(fillAttr?: number): void {
|
||||
public fillViewportRows(fillAttr?: IAttributeData): void {
|
||||
if (this.lines.length === 0) {
|
||||
if (fillAttr === undefined) {
|
||||
fillAttr = DEFAULT_ATTR;
|
||||
fillAttr = DEFAULT_ATTR_DATA;
|
||||
}
|
||||
let i = this._rows;
|
||||
while (i--) {
|
||||
@@ -131,6 +166,9 @@ export class Buffer implements IBuffer {
|
||||
* @param newRows The new number of rows.
|
||||
*/
|
||||
public resize(newCols: number, newRows: number): void {
|
||||
// store reference to null cell with default attrs
|
||||
const nullCell = this.getNullCell(DEFAULT_ATTR_DATA);
|
||||
|
||||
// Increase max length if needed before adjustments to allow space to fill
|
||||
// as required.
|
||||
const newMaxLength = this._getCorrectBufferLength(newRows);
|
||||
@@ -144,7 +182,7 @@ export class Buffer implements IBuffer {
|
||||
// Deal with columns increasing (reducing needs to happen after reflow)
|
||||
if (this._cols < newCols) {
|
||||
for (let i = 0; i < this.lines.length; i++) {
|
||||
this.lines.get(i).resize(newCols, FILL_CHAR_DATA);
|
||||
this.lines.get(i).resize(newCols, nullCell);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +203,7 @@ export class Buffer implements IBuffer {
|
||||
} else {
|
||||
// Add a blank line if there is no buffer left at the top to scroll to, or if there
|
||||
// are blank lines after the cursor
|
||||
this.lines.push(new BufferLine(newCols, FILL_CHAR_DATA));
|
||||
this.lines.push(new BufferLine(newCols, nullCell));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,7 +255,7 @@ export class Buffer implements IBuffer {
|
||||
// Trim the end of the line off if cols shrunk
|
||||
if (this._cols > newCols) {
|
||||
for (let i = 0; i < this.lines.length; i++) {
|
||||
this.lines.get(i).resize(newCols, FILL_CHAR_DATA);
|
||||
this.lines.get(i).resize(newCols, nullCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,7 +265,7 @@ export class Buffer implements IBuffer {
|
||||
}
|
||||
|
||||
private get _isReflowEnabled(): boolean {
|
||||
return this._hasScrollback && !(this._terminal as any).isWinptyCompatEnabled;
|
||||
return this._hasScrollback && !this._terminal.options.windowsMode;
|
||||
}
|
||||
|
||||
private _reflow(newCols: number, newRows: number): void {
|
||||
@@ -244,7 +282,7 @@ export class Buffer implements IBuffer {
|
||||
}
|
||||
|
||||
private _reflowLarger(newCols: number, newRows: number): void {
|
||||
const toRemove: number[] = reflowLargerGetLinesToRemove(this.lines, newCols, this.ybase + this.y);
|
||||
const toRemove: number[] = reflowLargerGetLinesToRemove(this.lines, this._cols, newCols, this.ybase + this.y, this.getNullCell(DEFAULT_ATTR_DATA));
|
||||
if (toRemove.length > 0) {
|
||||
const newLayoutResult = reflowLargerCreateNewLayout(this.lines, toRemove);
|
||||
reflowLargerApplyNewLayout(this.lines, newLayoutResult.layout);
|
||||
@@ -253,6 +291,7 @@ export class Buffer implements IBuffer {
|
||||
}
|
||||
|
||||
private _reflowLargerAdjustViewport(newCols: number, newRows: number, countRemoved: number): void {
|
||||
const nullCell = this.getNullCell(DEFAULT_ATTR_DATA);
|
||||
// Adjust viewport based on number of items removed
|
||||
let viewportAdjustments = countRemoved;
|
||||
while (viewportAdjustments-- > 0) {
|
||||
@@ -262,7 +301,7 @@ export class Buffer implements IBuffer {
|
||||
}
|
||||
if (this.lines.length < newRows) {
|
||||
// Add an extra row at the bottom of the viewport
|
||||
this.lines.push(new BufferLine(newCols, FILL_CHAR_DATA));
|
||||
this.lines.push(new BufferLine(newCols, nullCell));
|
||||
}
|
||||
} else {
|
||||
if (this.ydisp === this.ybase) {
|
||||
@@ -274,6 +313,7 @@ export class Buffer implements IBuffer {
|
||||
}
|
||||
|
||||
private _reflowSmaller(newCols: number, newRows: number): void {
|
||||
const nullCell = this.getNullCell(DEFAULT_ATTR_DATA);
|
||||
// Gather all BufferLines that need to be inserted into the Buffer here so that they can be
|
||||
// batched up and only committed once
|
||||
const toInsert = [];
|
||||
@@ -314,7 +354,7 @@ export class Buffer implements IBuffer {
|
||||
// Add the new lines
|
||||
const newLines: BufferLine[] = [];
|
||||
for (let i = 0; i < linesToAdd; i++) {
|
||||
const newLine = this.getBlankLine(DEFAULT_ATTR, true) as BufferLine;
|
||||
const newLine = this.getBlankLine(DEFAULT_ATTR_DATA, true) as BufferLine;
|
||||
newLines.push(newLine);
|
||||
}
|
||||
if (newLines.length > 0) {
|
||||
@@ -348,15 +388,15 @@ export class Buffer implements IBuffer {
|
||||
srcCol -= cellsToCopy;
|
||||
if (srcCol === 0) {
|
||||
srcLineIndex--;
|
||||
// TODO: srcCol shoudl take trimmed length into account
|
||||
srcCol = wrappedLines[Math.max(srcLineIndex, 0)].getTrimmedLength(); // this._cols;
|
||||
const wrappedLinesIndex = Math.max(srcLineIndex, 0);
|
||||
srcCol = getWrappedLineTrimmedLength(wrappedLines, wrappedLinesIndex, this._cols);
|
||||
}
|
||||
}
|
||||
|
||||
// Null out the end of the line ends if a wide character wrapped to the following line
|
||||
for (let i = 0; i < wrappedLines.length; i++) {
|
||||
if (destLineLengths[i] < newCols) {
|
||||
wrappedLines[i].set(destLineLengths[i], FILL_CHAR_DATA);
|
||||
wrappedLines[i].setCell(destLineLengths[i], nullCell);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+163
-99
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
import * as chai from 'chai';
|
||||
import { BufferLine } from './BufferLine';
|
||||
import { BufferLine, CellData, Content } from './BufferLine';
|
||||
import { CharData, IBufferLine } from './Types';
|
||||
import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR } from './Buffer';
|
||||
|
||||
@@ -16,12 +16,38 @@ class TestBufferLine extends BufferLine {
|
||||
public toArray(): CharData[] {
|
||||
const result = [];
|
||||
for (let i = 0; i < this.length; ++i) {
|
||||
result.push(this.get(i));
|
||||
result.push(this.loadCell(i, new CellData()).getAsCharData());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
describe('CellData', () => {
|
||||
it('CharData <--> CellData equality', () => {
|
||||
const cell = new CellData();
|
||||
// ASCII
|
||||
cell.setFromCharData([123, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
chai.assert.equal(cell.isCombined(), 0);
|
||||
// combining
|
||||
cell.setFromCharData([123, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
|
||||
chai.assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||||
// surrogate
|
||||
cell.setFromCharData([123, '𝄞', 1, 0x1D11E]);
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, '𝄞', 1, 0x1D11E]);
|
||||
chai.assert.equal(cell.isCombined(), 0);
|
||||
// surrogate + combining
|
||||
cell.setFromCharData([123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]);
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, '𓂀\u0301', 1, '𓂀\u0301'.charCodeAt(2)]);
|
||||
chai.assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||||
// wide char
|
||||
cell.setFromCharData([123, '1', 2, '1'.charCodeAt(0)]);
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, '1', 2, '1'.charCodeAt(0)]);
|
||||
chai.assert.equal(cell.isCombined(), 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('BufferLine', function(): void {
|
||||
it('ctor', function(): void {
|
||||
let line: IBufferLine = new TestBufferLine(0);
|
||||
@@ -29,23 +55,23 @@ describe('BufferLine', function(): void {
|
||||
chai.expect(line.isWrapped).equals(false);
|
||||
line = new TestBufferLine(10);
|
||||
chai.expect(line.length).equals(10);
|
||||
chai.expect(line.get(0)).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
chai.expect(line.loadCell(0, new CellData()).getAsCharData()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
chai.expect(line.isWrapped).equals(false);
|
||||
line = new TestBufferLine(10, null, true);
|
||||
chai.expect(line.length).equals(10);
|
||||
chai.expect(line.get(0)).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
chai.expect(line.loadCell(0, new CellData()).getAsCharData()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
chai.expect(line.isWrapped).equals(true);
|
||||
line = new TestBufferLine(10, [123, 'a', 456, 'a'.charCodeAt(0)], true);
|
||||
line = new TestBufferLine(10, CellData.fromCharData([123, 'a', 456, 'a'.charCodeAt(0)]), true);
|
||||
chai.expect(line.length).equals(10);
|
||||
chai.expect(line.get(0)).eql([123, 'a', 456, 'a'.charCodeAt(0)]);
|
||||
chai.expect(line.loadCell(0, new CellData()).getAsCharData()).eql([123, 'a', 456, 'a'.charCodeAt(0)]);
|
||||
chai.expect(line.isWrapped).equals(true);
|
||||
});
|
||||
it('insertCells', function(): void {
|
||||
const line = new TestBufferLine(3);
|
||||
line.set(0, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
line.set(1, [2, 'b', 0, 'b'.charCodeAt(0)]);
|
||||
line.set(2, [3, 'c', 0, 'c'.charCodeAt(0)]);
|
||||
line.insertCells(1, 3, [4, 'd', 0, 'd'.charCodeAt(0)]);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)]));
|
||||
line.insertCells(1, 3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
|
||||
chai.expect(line.toArray()).eql([
|
||||
[1, 'a', 0, 'a'.charCodeAt(0)],
|
||||
[4, 'd', 0, 'd'.charCodeAt(0)],
|
||||
@@ -54,12 +80,12 @@ describe('BufferLine', function(): void {
|
||||
});
|
||||
it('deleteCells', function(): void {
|
||||
const line = new TestBufferLine(5);
|
||||
line.set(0, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
line.set(1, [2, 'b', 0, 'b'.charCodeAt(0)]);
|
||||
line.set(2, [3, 'c', 0, 'c'.charCodeAt(0)]);
|
||||
line.set(3, [4, 'd', 0, 'd'.charCodeAt(0)]);
|
||||
line.set(4, [5, 'e', 0, 'e'.charCodeAt(0)]);
|
||||
line.deleteCells(1, 2, [6, 'f', 0, 'f'.charCodeAt(0)]);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)]));
|
||||
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
|
||||
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
|
||||
line.deleteCells(1, 2, CellData.fromCharData([6, 'f', 0, 'f'.charCodeAt(0)]));
|
||||
chai.expect(line.toArray()).eql([
|
||||
[1, 'a', 0, 'a'.charCodeAt(0)],
|
||||
[4, 'd', 0, 'd'.charCodeAt(0)],
|
||||
@@ -70,12 +96,12 @@ describe('BufferLine', function(): void {
|
||||
});
|
||||
it('replaceCells', function(): void {
|
||||
const line = new TestBufferLine(5);
|
||||
line.set(0, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
line.set(1, [2, 'b', 0, 'b'.charCodeAt(0)]);
|
||||
line.set(2, [3, 'c', 0, 'c'.charCodeAt(0)]);
|
||||
line.set(3, [4, 'd', 0, 'd'.charCodeAt(0)]);
|
||||
line.set(4, [5, 'e', 0, 'e'.charCodeAt(0)]);
|
||||
line.replaceCells(2, 4, [6, 'f', 0, 'f'.charCodeAt(0)]);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)]));
|
||||
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
|
||||
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
|
||||
line.replaceCells(2, 4, CellData.fromCharData([6, 'f', 0, 'f'.charCodeAt(0)]));
|
||||
chai.expect(line.toArray()).eql([
|
||||
[1, 'a', 0, 'a'.charCodeAt(0)],
|
||||
[2, 'b', 0, 'b'.charCodeAt(0)],
|
||||
@@ -86,12 +112,12 @@ describe('BufferLine', function(): void {
|
||||
});
|
||||
it('fill', function(): void {
|
||||
const line = new TestBufferLine(5);
|
||||
line.set(0, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
line.set(1, [2, 'b', 0, 'b'.charCodeAt(0)]);
|
||||
line.set(2, [3, 'c', 0, 'c'.charCodeAt(0)]);
|
||||
line.set(3, [4, 'd', 0, 'd'.charCodeAt(0)]);
|
||||
line.set(4, [5, 'e', 0, 'e'.charCodeAt(0)]);
|
||||
line.fill([123, 'z', 0, 'z'.charCodeAt(0)]);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)]));
|
||||
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
|
||||
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
|
||||
line.fill(CellData.fromCharData([123, 'z', 0, 'z'.charCodeAt(0)]));
|
||||
chai.expect(line.toArray()).eql([
|
||||
[123, 'z', 0, 'z'.charCodeAt(0)],
|
||||
[123, 'z', 0, 'z'.charCodeAt(0)],
|
||||
@@ -102,11 +128,11 @@ describe('BufferLine', function(): void {
|
||||
});
|
||||
it('clone', function(): void {
|
||||
const line = new TestBufferLine(5, null, true);
|
||||
line.set(0, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
line.set(1, [2, 'b', 0, 'b'.charCodeAt(0)]);
|
||||
line.set(2, [3, 'c', 0, 'c'.charCodeAt(0)]);
|
||||
line.set(3, [4, 'd', 0, 'd'.charCodeAt(0)]);
|
||||
line.set(4, [5, 'e', 0, 'e'.charCodeAt(0)]);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)]));
|
||||
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
|
||||
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
|
||||
const line2 = line.clone();
|
||||
chai.expect(TestBufferLine.prototype.toArray.apply(line2)).eql(line.toArray());
|
||||
chai.expect(line2.length).equals(line.length);
|
||||
@@ -114,12 +140,12 @@ describe('BufferLine', function(): void {
|
||||
});
|
||||
it('copyFrom', function(): void {
|
||||
const line = new TestBufferLine(5);
|
||||
line.set(0, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
line.set(1, [2, 'b', 0, 'b'.charCodeAt(0)]);
|
||||
line.set(2, [3, 'c', 0, 'c'.charCodeAt(0)]);
|
||||
line.set(3, [4, 'd', 0, 'd'.charCodeAt(0)]);
|
||||
line.set(4, [5, 'e', 0, 'e'.charCodeAt(0)]);
|
||||
const line2 = new TestBufferLine(5, [1, 'a', 0, 'a'.charCodeAt(0)], true);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)]));
|
||||
line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)]));
|
||||
line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)]));
|
||||
const line2 = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), true);
|
||||
line2.copyFrom(line);
|
||||
chai.expect(line2.toArray()).eql(line.toArray());
|
||||
chai.expect(line2.length).equals(line.length);
|
||||
@@ -129,9 +155,9 @@ describe('BufferLine', function(): void {
|
||||
// CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print
|
||||
// --> set code to the last charCodeAt value of the string
|
||||
// Note: needs to be fixed once the string pointer is in place
|
||||
const line = new TestBufferLine(2, [1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]);
|
||||
const line = new TestBufferLine(2, CellData.fromCharData([1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]));
|
||||
chai.expect(line.toArray()).eql([[1, 'e\u0301', 0, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]]);
|
||||
const line2 = new TestBufferLine(5, [1, 'a', 0, '\u0301'.charCodeAt(0)], true);
|
||||
const line2 = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, '\u0301'.charCodeAt(0)]), true);
|
||||
line2.copyFrom(line);
|
||||
chai.expect(line2.toArray()).eql(line.toArray());
|
||||
const line3 = line.clone();
|
||||
@@ -139,81 +165,81 @@ describe('BufferLine', function(): void {
|
||||
});
|
||||
describe('resize', function(): void {
|
||||
it('enlarge(false)', function(): void {
|
||||
const line = new TestBufferLine(5, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
const line = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
|
||||
line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
chai.expect(line.toArray()).eql(Array(10).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('enlarge(true)', function(): void {
|
||||
const line = new TestBufferLine(5, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
const line = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
|
||||
line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
chai.expect(line.toArray()).eql(Array(10).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink(true) - should apply new size', function(): void {
|
||||
const line = new TestBufferLine(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(5, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
|
||||
line.resize(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
chai.expect(line.toArray()).eql(Array(5).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink to 0 length', function(): void {
|
||||
const line = new TestBufferLine(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(0, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
|
||||
line.resize(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
chai.expect(line.toArray()).eql(Array(0).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('should remove combining data on replaced cells after shrinking then enlarging', () => {
|
||||
const line = new TestBufferLine(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
|
||||
line.set(2, [ null, '😁', 1, '😁'.charCodeAt(0) ]);
|
||||
line.set(9, [ null, '😁', 1, '😁'.charCodeAt(0) ]);
|
||||
chai.expect(line.translateToString()).eql('aa😁aaaaaa😁');
|
||||
chai.expect(Object.keys(line.combined).length).eql(2);
|
||||
line.resize(5, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
line.resize(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
chai.expect(line.translateToString()).eql('aa😁aa');
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
chai.expect(line.translateToString()).eql('aa😁aaaaaaa');
|
||||
chai.expect(Object.keys(line.combined).length).eql(1);
|
||||
});
|
||||
});
|
||||
describe('getTrimLength', function(): void {
|
||||
it('empty line', function(): void {
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE], false);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
chai.expect(line.getTrimmedLength()).equal(0);
|
||||
});
|
||||
it('ASCII', function(): void {
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE], false);
|
||||
line.set(0, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(2, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.expect(line.getTrimmedLength()).equal(3);
|
||||
});
|
||||
it('surrogate', function(): void {
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE], false);
|
||||
line.set(0, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(2, [1, '𝄞', 1, '𝄞'.charCodeAt(0)]);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)]));
|
||||
chai.expect(line.getTrimmedLength()).equal(3);
|
||||
});
|
||||
it('combining', function(): void {
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE], false);
|
||||
line.set(0, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(2, [1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]));
|
||||
chai.expect(line.getTrimmedLength()).equal(3);
|
||||
});
|
||||
it('fullwidth', function(): void {
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE], false);
|
||||
line.set(0, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(2, [1, '1', 2, '1'.charCodeAt(0)]);
|
||||
line.set(3, [0, '', 0, undefined]);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)]));
|
||||
line.setCell(3, CellData.fromCharData([0, '', 0, undefined]));
|
||||
chai.expect(line.getTrimmedLength()).equal(4); // also counts null cell after fullwidth
|
||||
});
|
||||
});
|
||||
describe('translateToString with and w\'o trimming', function(): void {
|
||||
it('empty line', function(): void {
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE], false);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
chai.expect(line.translateToString(false)).equal(' ');
|
||||
chai.expect(line.translateToString(true)).equal('');
|
||||
});
|
||||
it('ASCII', function(): void {
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE], false);
|
||||
line.set(0, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(2, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(4, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(5, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(4, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(5, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.expect(line.translateToString(false)).equal('a a aa ');
|
||||
chai.expect(line.translateToString(true)).equal('a a aa');
|
||||
chai.expect(line.translateToString(false, 0, 5)).equal('a a a');
|
||||
@@ -225,11 +251,11 @@ describe('BufferLine', function(): void {
|
||||
|
||||
});
|
||||
it('surrogate', function(): void {
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE], false);
|
||||
line.set(0, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(2, [1, '𝄞', 1, '𝄞'.charCodeAt(0)]);
|
||||
line.set(4, [1, '𝄞', 1, '𝄞'.charCodeAt(0)]);
|
||||
line.set(5, [1, '𝄞', 1, '𝄞'.charCodeAt(0)]);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)]));
|
||||
line.setCell(4, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)]));
|
||||
line.setCell(5, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)]));
|
||||
chai.expect(line.translateToString(false)).equal('a 𝄞 𝄞𝄞 ');
|
||||
chai.expect(line.translateToString(true)).equal('a 𝄞 𝄞𝄞');
|
||||
chai.expect(line.translateToString(false, 0, 5)).equal('a 𝄞 𝄞');
|
||||
@@ -240,11 +266,11 @@ describe('BufferLine', function(): void {
|
||||
chai.expect(line.translateToString(true, 0, 3)).equal('a 𝄞');
|
||||
});
|
||||
it('combining', function(): void {
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE], false);
|
||||
line.set(0, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(2, [1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
|
||||
line.set(4, [1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
|
||||
line.set(5, [1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]));
|
||||
line.setCell(4, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]));
|
||||
line.setCell(5, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)]));
|
||||
chai.expect(line.translateToString(false)).equal('a e\u0301 e\u0301e\u0301 ');
|
||||
chai.expect(line.translateToString(true)).equal('a e\u0301 e\u0301e\u0301');
|
||||
chai.expect(line.translateToString(false, 0, 5)).equal('a e\u0301 e\u0301');
|
||||
@@ -255,14 +281,14 @@ describe('BufferLine', function(): void {
|
||||
chai.expect(line.translateToString(true, 0, 3)).equal('a e\u0301');
|
||||
});
|
||||
it('fullwidth', function(): void {
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE], false);
|
||||
line.set(0, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(2, [1, '1', 2, '1'.charCodeAt(0)]);
|
||||
line.set(3, [0, '', 0, undefined]);
|
||||
line.set(5, [1, '1', 2, '1'.charCodeAt(0)]);
|
||||
line.set(6, [0, '', 0, undefined]);
|
||||
line.set(7, [1, '1', 2, '1'.charCodeAt(0)]);
|
||||
line.set(8, [0, '', 0, undefined]);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)]));
|
||||
line.setCell(3, CellData.fromCharData([0, '', 0, undefined]));
|
||||
line.setCell(5, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)]));
|
||||
line.setCell(6, CellData.fromCharData([0, '', 0, undefined]));
|
||||
line.setCell(7, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)]));
|
||||
line.setCell(8, CellData.fromCharData([0, '', 0, undefined]));
|
||||
chai.expect(line.translateToString(false)).equal('a 1 11 ');
|
||||
chai.expect(line.translateToString(true)).equal('a 1 11');
|
||||
chai.expect(line.translateToString(false, 0, 7)).equal('a 1 1');
|
||||
@@ -279,12 +305,12 @@ describe('BufferLine', function(): void {
|
||||
chai.expect(line.translateToString(true, 0, 2)).equal('a ');
|
||||
});
|
||||
it('space at end', function(): void {
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE], false);
|
||||
line.set(0, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(2, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(4, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(5, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
line.set(6, [1, ' ', 1, ' '.charCodeAt(0)]);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(4, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(5, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
line.setCell(6, CellData.fromCharData([1, ' ', 1, ' '.charCodeAt(0)]));
|
||||
chai.expect(line.translateToString(false)).equal('a a aa ');
|
||||
chai.expect(line.translateToString(true)).equal('a a aa ');
|
||||
});
|
||||
@@ -292,14 +318,52 @@ describe('BufferLine', function(): void {
|
||||
// sanity check - broken line with invalid out of bound null width cells
|
||||
// this can atm happen with deleting/inserting chars in inputhandler by "breaking"
|
||||
// fullwidth pairs --> needs to be fixed after settling BufferLine impl
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE], false);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
chai.expect(line.translateToString(false)).equal(' ');
|
||||
chai.expect(line.translateToString(true)).equal('');
|
||||
});
|
||||
it('should work with endCol=0', () => {
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE], false);
|
||||
line.set(0, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)]));
|
||||
chai.expect(line.translateToString(true, 0, 0)).equal('');
|
||||
});
|
||||
});
|
||||
describe('addCharToCell', () => {
|
||||
it('should set width to 1 for empty cell', () => {
|
||||
const line = new TestBufferLine(3, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
line.addCodepointToCell(0, '\u0301'.charCodeAt(0));
|
||||
const cell = line.loadCell(0, new CellData());
|
||||
// chars contains single combining char
|
||||
// width is set to 1
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [DEFAULT_ATTR, '\u0301', 1, 0x0301]);
|
||||
// do not account a single combining char as combined
|
||||
chai.assert.equal(cell.isCombined(), 0);
|
||||
});
|
||||
it('should add char to combining string in cell', () => {
|
||||
const line = new TestBufferLine(3, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
const cell = line .loadCell(0, new CellData());
|
||||
cell.setFromCharData([123, 'e\u0301', 1, 'e\u0301'.charCodeAt(1)]);
|
||||
line.setCell(0, cell);
|
||||
line.addCodepointToCell(0, '\u0301'.charCodeAt(0));
|
||||
line.loadCell(0, cell);
|
||||
// chars contains 3 chars
|
||||
// width is set to 1
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301\u0301', 1, 0x0301]);
|
||||
// do not account a single combining char as combined
|
||||
chai.assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||||
});
|
||||
it('should create combining string on taken cell', () => {
|
||||
const line = new TestBufferLine(3, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
const cell = line .loadCell(0, new CellData());
|
||||
cell.setFromCharData([123, 'e', 1, 'e'.charCodeAt(1)]);
|
||||
line.setCell(0, cell);
|
||||
line.addCodepointToCell(0, '\u0301'.charCodeAt(0));
|
||||
line.loadCell(0, cell);
|
||||
// chars contains 2 chars
|
||||
// width is set to 1
|
||||
chai.assert.deepEqual(cell.getAsCharData(), [123, 'e\u0301', 1, 0x0301]);
|
||||
// do not account a single combining char as combined
|
||||
chai.assert.equal(cell.isCombined(), Content.IS_COMBINED_MASK);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+458
-50
File diff suppressed because it is too large
Load Diff
+24
-9
@@ -3,10 +3,9 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { FILL_CHAR_DATA } from './Buffer';
|
||||
import { BufferLine } from './BufferLine';
|
||||
import { CircularList, IDeleteEvent } from './common/CircularList';
|
||||
import { IBufferLine } from './Types';
|
||||
import { IBufferLine, ICellData } from './Types';
|
||||
|
||||
export interface INewLayoutResult {
|
||||
layout: number[];
|
||||
@@ -19,7 +18,7 @@ export interface INewLayoutResult {
|
||||
* @param lines The buffer lines.
|
||||
* @param newCols The columns after resize.
|
||||
*/
|
||||
export function reflowLargerGetLinesToRemove(lines: CircularList<IBufferLine>, newCols: number, bufferAbsoluteY: number): number[] {
|
||||
export function reflowLargerGetLinesToRemove(lines: CircularList<IBufferLine>, oldCols: number, newCols: number, bufferAbsoluteY: number, nullCell: ICellData): number[] {
|
||||
// Gather all BufferLines that need to be removed from the Buffer here so that they can be
|
||||
// batched up and only committed once
|
||||
const toRemove: number[] = [];
|
||||
@@ -48,11 +47,11 @@ export function reflowLargerGetLinesToRemove(lines: CircularList<IBufferLine>, n
|
||||
|
||||
// Copy buffer data to new locations
|
||||
let destLineIndex = 0;
|
||||
let destCol = wrappedLines[destLineIndex].getTrimmedLength();
|
||||
let destCol = getWrappedLineTrimmedLength(wrappedLines, destLineIndex, oldCols);
|
||||
let srcLineIndex = 1;
|
||||
let srcCol = 0;
|
||||
while (srcLineIndex < wrappedLines.length) {
|
||||
const srcTrimmedTineLength = wrappedLines[srcLineIndex].getTrimmedLength();
|
||||
const srcTrimmedTineLength = getWrappedLineTrimmedLength(wrappedLines, srcLineIndex, oldCols);
|
||||
const srcRemainingCells = srcTrimmedTineLength - srcCol;
|
||||
const destRemainingCells = newCols - destCol;
|
||||
const cellsToCopy = Math.min(srcRemainingCells, destRemainingCells);
|
||||
@@ -75,13 +74,13 @@ export function reflowLargerGetLinesToRemove(lines: CircularList<IBufferLine>, n
|
||||
if (wrappedLines[destLineIndex - 1].getWidth(newCols - 1) === 2) {
|
||||
wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[destLineIndex - 1], newCols - 1, destCol++, 1, false);
|
||||
// Null out the end of the last row
|
||||
wrappedLines[destLineIndex - 1].set(newCols - 1, FILL_CHAR_DATA);
|
||||
wrappedLines[destLineIndex - 1].setCell(newCols - 1, nullCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear out remaining cells or fragments could remain;
|
||||
wrappedLines[destLineIndex].replaceCells(destCol, newCols, FILL_CHAR_DATA);
|
||||
wrappedLines[destLineIndex].replaceCells(destCol, newCols, nullCell);
|
||||
|
||||
// Work backwards and remove any rows at the end that only contain null cells
|
||||
let countToRemove = 0;
|
||||
@@ -173,7 +172,7 @@ export function reflowLargerApplyNewLayout(lines: CircularList<IBufferLine>, new
|
||||
*/
|
||||
export function reflowSmallerGetNewLineLengths(wrappedLines: BufferLine[], oldCols: number, newCols: number): number[] {
|
||||
const newLineLengths: number[] = [];
|
||||
const cellsNeeded = wrappedLines.map(l => l.getTrimmedLength()).reduce((p, c) => p + c);
|
||||
const cellsNeeded = wrappedLines.map((l, i) => getWrappedLineTrimmedLength(wrappedLines, i, oldCols)).reduce((p, c) => p + c);
|
||||
|
||||
// Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and
|
||||
// linesNeeded
|
||||
@@ -187,7 +186,7 @@ export function reflowSmallerGetNewLineLengths(wrappedLines: BufferLine[], oldCo
|
||||
break;
|
||||
}
|
||||
srcCol += newCols;
|
||||
const oldTrimmedLength = wrappedLines[srcLine].getTrimmedLength();
|
||||
const oldTrimmedLength = getWrappedLineTrimmedLength(wrappedLines, srcLine, oldCols);
|
||||
if (srcCol > oldTrimmedLength) {
|
||||
srcCol -= oldTrimmedLength;
|
||||
srcLine++;
|
||||
@@ -203,3 +202,19 @@ export function reflowSmallerGetNewLineLengths(wrappedLines: BufferLine[], oldCo
|
||||
|
||||
return newLineLengths;
|
||||
}
|
||||
|
||||
export function getWrappedLineTrimmedLength(lines: BufferLine[], i: number, cols: number): number {
|
||||
// If this is the last row in the wrapped line, get the actual trimmed length
|
||||
if (i === lines.length - 1) {
|
||||
return lines[i].getTrimmedLength();
|
||||
}
|
||||
// Detect whether the following line starts with a wide character and the end of the current line
|
||||
// is null, if so then we can be pretty sure the null character should be excluded from the line
|
||||
// length]
|
||||
const endsInNull = !(lines[i].hasContent(cols - 1)) && lines[i].getWidth(cols - 1) === 1;
|
||||
const followingLineStartsWithWide = lines[i + 1].getWidth(0) === 2;
|
||||
if (endsInNull && followingLineStartsWithWide) {
|
||||
return cols - 1;
|
||||
}
|
||||
return cols;
|
||||
}
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ITerminal, IBufferSet } from './Types';
|
||||
import { ITerminal, IBufferSet, IAttributeData } from './Types';
|
||||
import { Buffer } from './Buffer';
|
||||
import { EventEmitter } from './common/EventEmitter';
|
||||
|
||||
@@ -77,7 +77,7 @@ export class BufferSet extends EventEmitter implements IBufferSet {
|
||||
/**
|
||||
* Sets the alt Buffer of the BufferSet as its currently active Buffer
|
||||
*/
|
||||
public activateAltBuffer(fillAttr?: number): void {
|
||||
public activateAltBuffer(fillAttr?: IAttributeData): void {
|
||||
if (this._activeBuffer === this._alt) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { assert } from 'chai';
|
||||
import { getStringCellWidth, wcwidth } from './CharWidth';
|
||||
import { IBuffer } from './Types';
|
||||
import { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from './Buffer';
|
||||
import { CellData } from './BufferLine';
|
||||
|
||||
|
||||
describe('getStringCellWidth', function(): void {
|
||||
@@ -22,7 +23,7 @@ describe('getStringCellWidth', function(): void {
|
||||
for (let i = start; i < end; ++i) {
|
||||
const line = buffer.lines.get(i);
|
||||
for (let j = 0; j < line.length; ++j) { // TODO: change to trimBorder with multiline
|
||||
const ch = line.get(j);
|
||||
const ch = line.loadCell(j, new CellData()).getAsCharData();
|
||||
result += ch[CHAR_DATA_WIDTH_INDEX];
|
||||
// return on sentinel
|
||||
if (ch[CHAR_DATA_CHAR_INDEX] === sentinel) {
|
||||
|
||||
+119
-14
@@ -5,32 +5,33 @@
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { InputHandler } from './InputHandler';
|
||||
import { MockInputHandlingTerminal } from './ui/TestUtils.test';
|
||||
import { CHAR_DATA_ATTR_INDEX, DEFAULT_ATTR } from './Buffer';
|
||||
import { MockInputHandlingTerminal, TestTerminal } from './ui/TestUtils.test';
|
||||
import { DEFAULT_ATTR_DATA } from './Buffer';
|
||||
import { Terminal } from './Terminal';
|
||||
import { IBufferLine } from './Types';
|
||||
import { CellData, Attributes, AttributeData } from './BufferLine';
|
||||
|
||||
describe('InputHandler', () => {
|
||||
describe('save and restore cursor', () => {
|
||||
const terminal = new MockInputHandlingTerminal();
|
||||
terminal.buffer.x = 1;
|
||||
terminal.buffer.y = 2;
|
||||
terminal.curAttr = 3;
|
||||
terminal.curAttrData.fg = 3;
|
||||
const inputHandler = new InputHandler(terminal);
|
||||
// Save cursor position
|
||||
inputHandler.saveCursor([]);
|
||||
assert.equal(terminal.buffer.x, 1);
|
||||
assert.equal(terminal.buffer.y, 2);
|
||||
assert.equal(terminal.curAttr, 3);
|
||||
assert.equal(terminal.curAttrData.fg, 3);
|
||||
// Change cursor position
|
||||
terminal.buffer.x = 10;
|
||||
terminal.buffer.y = 20;
|
||||
terminal.curAttr = 30;
|
||||
terminal.curAttrData.fg = 30;
|
||||
// Restore cursor position
|
||||
inputHandler.restoreCursor([]);
|
||||
assert.equal(terminal.buffer.x, 1);
|
||||
assert.equal(terminal.buffer.y, 2);
|
||||
assert.equal(terminal.curAttr, 3);
|
||||
assert.equal(terminal.curAttrData.fg, 3);
|
||||
});
|
||||
describe('setCursorStyle', () => {
|
||||
it('should call Terminal.setOption with correct params', () => {
|
||||
@@ -356,45 +357,149 @@ describe('InputHandler', () => {
|
||||
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('');
|
||||
expect(term.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
|
||||
// Text color of 'TEST' should be red
|
||||
expect((term.buffer.lines.get(1).get(4)[CHAR_DATA_ATTR_INDEX] >> 9) & 0x1ff).to.equal(1);
|
||||
expect((term.buffer.lines.get(1).loadCell(4, new CellData()).getFgColor())).to.equal(1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1047 (alt screen buffer)', () => {
|
||||
handler.parse('\x1b[?1047h\r\n\x1b[31mJUNK\x1b[?1047lTEST');
|
||||
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('');
|
||||
expect(term.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
|
||||
// Text color of 'TEST' should be red
|
||||
expect((term.buffer.lines.get(1).get(4)[CHAR_DATA_ATTR_INDEX] >> 9) & 0x1ff).to.equal(1);
|
||||
expect((term.buffer.lines.get(1).loadCell(4, new CellData()).getFgColor())).to.equal(1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1048 (alt screen cursor)', () => {
|
||||
handler.parse('\x1b[?1048h\r\n\x1b[31mJUNK\x1b[?1048lTEST');
|
||||
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
||||
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('JUNK');
|
||||
// Text color of 'TEST' should be default
|
||||
expect(term.buffer.lines.get(0).get(0)[CHAR_DATA_ATTR_INDEX]).to.equal(DEFAULT_ATTR);
|
||||
expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
|
||||
// Text color of 'JUNK' should be red
|
||||
expect((term.buffer.lines.get(1).get(0)[CHAR_DATA_ATTR_INDEX] >> 9) & 0x1ff).to.equal(1);
|
||||
expect((term.buffer.lines.get(1).loadCell(0, new CellData()).getFgColor())).to.equal(1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1049 (alt screen buffer+cursor)', () => {
|
||||
handler.parse('\x1b[?1049h\r\n\x1b[31mJUNK\x1b[?1049lTEST');
|
||||
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
||||
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('');
|
||||
// Text color of 'TEST' should be default
|
||||
expect(term.buffer.lines.get(0).get(0)[CHAR_DATA_ATTR_INDEX]).to.equal(DEFAULT_ATTR);
|
||||
expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1049 - maintains saved cursor for alt buffer', () => {
|
||||
handler.parse('\x1b[?1049h\r\n\x1b[31m\x1b[s\x1b[?1049lTEST');
|
||||
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
||||
// Text color of 'TEST' should be default
|
||||
expect(term.buffer.lines.get(0).get(0)[CHAR_DATA_ATTR_INDEX]).to.equal(DEFAULT_ATTR);
|
||||
expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
|
||||
handler.parse('\x1b[?1049h\x1b[uTEST');
|
||||
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('TEST');
|
||||
// Text color of 'TEST' should be red
|
||||
expect((term.buffer.lines.get(1).get(0)[CHAR_DATA_ATTR_INDEX] >> 9) & 0x1ff).to.equal(1);
|
||||
expect((term.buffer.lines.get(1).loadCell(0, new CellData()).getFgColor())).to.equal(1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1049 - clears alt buffer with erase attributes', () => {
|
||||
handler.parse('\x1b[42m\x1b[?1049h');
|
||||
// Buffer should be filled with green background
|
||||
expect(term.buffer.lines.get(20).get(10)[CHAR_DATA_ATTR_INDEX] & 0x1ff).to.equal(2);
|
||||
expect(term.buffer.lines.get(20).loadCell(10, new CellData()).getBgColor()).to.equal(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('text attributes', () => {
|
||||
let term: TestTerminal;
|
||||
beforeEach(() => {
|
||||
term = new TestTerminal();
|
||||
});
|
||||
it('bold', () => {
|
||||
term.writeSync('\x1b[1m');
|
||||
assert.equal(!!term.curAttrData.isBold(), true);
|
||||
term.writeSync('\x1b[22m');
|
||||
assert.equal(!!term.curAttrData.isBold(), false);
|
||||
});
|
||||
it('dim', () => {
|
||||
term.writeSync('\x1b[2m');
|
||||
assert.equal(!!term.curAttrData.isDim(), true);
|
||||
term.writeSync('\x1b[22m');
|
||||
assert.equal(!!term.curAttrData.isDim(), false);
|
||||
});
|
||||
it('italic', () => {
|
||||
term.writeSync('\x1b[3m');
|
||||
assert.equal(!!term.curAttrData.isItalic(), true);
|
||||
term.writeSync('\x1b[23m');
|
||||
assert.equal(!!term.curAttrData.isItalic(), false);
|
||||
});
|
||||
it('underline', () => {
|
||||
term.writeSync('\x1b[4m');
|
||||
assert.equal(!!term.curAttrData.isUnderline(), true);
|
||||
term.writeSync('\x1b[24m');
|
||||
assert.equal(!!term.curAttrData.isUnderline(), false);
|
||||
});
|
||||
it('blink', () => {
|
||||
term.writeSync('\x1b[5m');
|
||||
assert.equal(!!term.curAttrData.isBlink(), true);
|
||||
term.writeSync('\x1b[25m');
|
||||
assert.equal(!!term.curAttrData.isBlink(), false);
|
||||
});
|
||||
it('inverse', () => {
|
||||
term.writeSync('\x1b[7m');
|
||||
assert.equal(!!term.curAttrData.isInverse(), true);
|
||||
term.writeSync('\x1b[27m');
|
||||
assert.equal(!!term.curAttrData.isInverse(), false);
|
||||
});
|
||||
it('invisible', () => {
|
||||
term.writeSync('\x1b[8m');
|
||||
assert.equal(!!term.curAttrData.isInvisible(), true);
|
||||
term.writeSync('\x1b[28m');
|
||||
assert.equal(!!term.curAttrData.isInvisible(), false);
|
||||
});
|
||||
it('colormode palette 16', () => {
|
||||
assert.equal(term.curAttrData.getFgColorMode(), 0); // DEFAULT
|
||||
assert.equal(term.curAttrData.getBgColorMode(), 0); // DEFAULT
|
||||
// lower 8 colors
|
||||
for (let i = 0; i < 8; ++i) {
|
||||
term.writeSync(`\x1b[${i + 30};${i + 40}m`);
|
||||
assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P16);
|
||||
assert.equal(term.curAttrData.getFgColor(), i);
|
||||
assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P16);
|
||||
assert.equal(term.curAttrData.getBgColor(), i);
|
||||
}
|
||||
// reset to DEFAULT
|
||||
term.writeSync(`\x1b[39;49m`);
|
||||
assert.equal(term.curAttrData.getFgColorMode(), 0);
|
||||
assert.equal(term.curAttrData.getBgColorMode(), 0);
|
||||
});
|
||||
it('colormode palette 256', () => {
|
||||
assert.equal(term.curAttrData.getFgColorMode(), 0); // DEFAULT
|
||||
assert.equal(term.curAttrData.getBgColorMode(), 0); // DEFAULT
|
||||
// lower 8 colors
|
||||
for (let i = 0; i < 256; ++i) {
|
||||
term.writeSync(`\x1b[38;5;${i};48;5;${i}m`);
|
||||
assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P256);
|
||||
assert.equal(term.curAttrData.getFgColor(), i);
|
||||
assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P256);
|
||||
assert.equal(term.curAttrData.getBgColor(), i);
|
||||
}
|
||||
// reset to DEFAULT
|
||||
term.writeSync(`\x1b[39;49m`);
|
||||
assert.equal(term.curAttrData.getFgColorMode(), 0);
|
||||
assert.equal(term.curAttrData.getFgColor(), -1);
|
||||
assert.equal(term.curAttrData.getBgColorMode(), 0);
|
||||
assert.equal(term.curAttrData.getBgColor(), -1);
|
||||
});
|
||||
it('colormode RGB', () => {
|
||||
assert.equal(term.curAttrData.getFgColorMode(), 0); // DEFAULT
|
||||
assert.equal(term.curAttrData.getBgColorMode(), 0); // DEFAULT
|
||||
term.writeSync(`\x1b[38;2;1;2;3;48;2;4;5;6m`);
|
||||
assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_RGB);
|
||||
assert.equal(term.curAttrData.getFgColor(), 1 << 16 | 2 << 8 | 3);
|
||||
assert.deepEqual(AttributeData.toColorRGB(term.curAttrData.getFgColor()), [1, 2, 3]);
|
||||
assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_RGB);
|
||||
assert.deepEqual(AttributeData.toColorRGB(term.curAttrData.getBgColor()), [4, 5, 6]);
|
||||
// reset to DEFAULT
|
||||
term.writeSync(`\x1b[39;49m`);
|
||||
assert.equal(term.curAttrData.getFgColorMode(), 0);
|
||||
assert.equal(term.curAttrData.getFgColor(), -1);
|
||||
assert.equal(term.curAttrData.getBgColorMode(), 0);
|
||||
assert.equal(term.curAttrData.getBgColor(), -1);
|
||||
});
|
||||
it('should zero missing RGB values', () => {
|
||||
term.writeSync(`\x1b[38;2;1;2;3m`);
|
||||
term.writeSync(`\x1b[38;2;5m`);
|
||||
assert.deepEqual(AttributeData.toColorRGB(term.curAttrData.getFgColor()), [5, 0, 0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+89
-105
@@ -4,18 +4,17 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IInputHandler, IDcsHandler, IEscapeSequenceParser, IBuffer, IInputHandlingTerminal } from './Types';
|
||||
import { IInputHandler, IDcsHandler, IEscapeSequenceParser, IInputHandlingTerminal } from './Types';
|
||||
import { C0, C1 } from './common/data/EscapeSequences';
|
||||
import { CHARSETS, DEFAULT_CHARSET } from './core/data/Charsets';
|
||||
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX, DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE } from './Buffer';
|
||||
import { FLAGS } from './renderer/Types';
|
||||
import { NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR_DATA } from './Buffer';
|
||||
import { wcwidth } from './CharWidth';
|
||||
import { EscapeSequenceParser } from './EscapeSequenceParser';
|
||||
import { ICharset } from './core/Types';
|
||||
import { IDisposable } from 'xterm';
|
||||
import { Disposable } from './common/Lifecycle';
|
||||
import { concat } from './common/TypedArrayUtils';
|
||||
import { StringToUtf32, stringFromCodePoint, utf32ToString } from './core/input/TextDecoder';
|
||||
import { CellData, Attributes, FgFlags, BgFlags, AttributeData } from './BufferLine';
|
||||
|
||||
/**
|
||||
* Map collect to glevel. Used in `selectCharset`.
|
||||
@@ -105,6 +104,7 @@ class DECRQSS implements IDcsHandler {
|
||||
export class InputHandler extends Disposable implements IInputHandler {
|
||||
private _parseBuffer: Uint32Array = new Uint32Array(4096);
|
||||
private _stringDecoder: StringToUtf32 = new StringToUtf32();
|
||||
private _workCell: CellData = new CellData();
|
||||
|
||||
constructor(
|
||||
protected _terminal: IInputHandlingTerminal,
|
||||
@@ -301,9 +301,6 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
if (this._parseBuffer.length < data.length) {
|
||||
this._parseBuffer = new Uint32Array(data.length);
|
||||
}
|
||||
for (let i = 0; i < data.length; ++i) {
|
||||
this._parseBuffer[i] = data.charCodeAt(i);
|
||||
}
|
||||
this._parser.parse(this._parseBuffer, this._stringDecoder.decode(data, this._parseBuffer));
|
||||
|
||||
buffer = this._terminal.buffer;
|
||||
@@ -314,39 +311,36 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
public print(data: Uint32Array, start: number, end: number): void {
|
||||
let code: number;
|
||||
let char: string;
|
||||
let chWidth: number;
|
||||
const buffer: IBuffer = this._terminal.buffer;
|
||||
const charset: ICharset = this._terminal.charset;
|
||||
const screenReaderMode: boolean = this._terminal.options.screenReaderMode;
|
||||
const cols: number = this._terminal.cols;
|
||||
const wraparoundMode: boolean = this._terminal.wraparoundMode;
|
||||
const insertMode: boolean = this._terminal.insertMode;
|
||||
const curAttr: number = this._terminal.curAttr;
|
||||
const buffer = this._terminal.buffer;
|
||||
const charset = this._terminal.charset;
|
||||
const screenReaderMode = this._terminal.options.screenReaderMode;
|
||||
const cols = this._terminal.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);
|
||||
for (let pos = start; pos < end; ++pos) {
|
||||
code = data[pos];
|
||||
char = stringFromCodePoint(code);
|
||||
|
||||
// calculate print space
|
||||
// expensive call, therefore we save width in line buffer
|
||||
chWidth = wcwidth(code);
|
||||
|
||||
// get charset replacement character
|
||||
// charset are only defined for ASCII, therefore we only
|
||||
// charset is only defined for ASCII, therefore we only
|
||||
// search for an replacement char if code < 127
|
||||
if (code < 127 && charset) {
|
||||
const ch = charset[char];
|
||||
const ch = charset[String.fromCharCode(code)];
|
||||
if (ch) {
|
||||
code = ch.charCodeAt(0);
|
||||
char = ch;
|
||||
}
|
||||
}
|
||||
|
||||
if (screenReaderMode) {
|
||||
this._terminal.emit('a11y.char', char);
|
||||
this._terminal.emit('a11y.char', stringFromCodePoint(code));
|
||||
}
|
||||
|
||||
// insert combining char at last cursor position
|
||||
@@ -355,23 +349,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// since they always follow a cell consuming char
|
||||
// therefore we can test for buffer.x to avoid overflow left
|
||||
if (!chWidth && buffer.x) {
|
||||
const chMinusOne = bufferRow.get(buffer.x - 1);
|
||||
if (chMinusOne) {
|
||||
if (!chMinusOne[CHAR_DATA_WIDTH_INDEX]) {
|
||||
// found empty cell after fullwidth, need to go 2 cells back
|
||||
// it is save to step 2 cells back here
|
||||
// since an empty cell is only set by fullwidth chars
|
||||
const chMinusTwo = bufferRow.get(buffer.x - 2);
|
||||
if (chMinusTwo) {
|
||||
chMinusTwo[CHAR_DATA_CHAR_INDEX] += char;
|
||||
chMinusTwo[CHAR_DATA_CODE_INDEX] = code;
|
||||
bufferRow.set(buffer.x - 2, chMinusTwo); // must be set explicitly now
|
||||
}
|
||||
} else {
|
||||
chMinusOne[CHAR_DATA_CHAR_INDEX] += char;
|
||||
chMinusOne[CHAR_DATA_CODE_INDEX] = code;
|
||||
bufferRow.set(buffer.x - 1, chMinusOne); // must be set explicitly now
|
||||
}
|
||||
if (!bufferRow.getWidth(buffer.x - 1)) {
|
||||
// found empty cell after fullwidth, need to go 2 cells back
|
||||
// it is save to step 2 cells back here
|
||||
// since an empty cell is only set by fullwidth chars
|
||||
bufferRow.addCodepointToCell(buffer.x - 2, code);
|
||||
} else {
|
||||
bufferRow.addCodepointToCell(buffer.x - 1, code);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -410,25 +394,25 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// insert mode: move characters to right
|
||||
if (insertMode) {
|
||||
// right shift cells according to the width
|
||||
bufferRow.insertCells(buffer.x, chWidth, [curAttr, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
bufferRow.insertCells(buffer.x, chWidth, buffer.getNullCell(curAttr));
|
||||
// test last cell - since the last cell has only room for
|
||||
// a halfwidth char any fullwidth shifted there is lost
|
||||
// and will be set to eraseChar
|
||||
const lastCell = bufferRow.get(cols - 1);
|
||||
if (lastCell[CHAR_DATA_WIDTH_INDEX] === 2) {
|
||||
bufferRow.set(cols - 1, [curAttr, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
// and will be set to empty cell
|
||||
if (bufferRow.getWidth(cols - 1) === 2) {
|
||||
bufferRow.setCellFromCodePoint(cols - 1, NULL_CELL_CODE, NULL_CELL_WIDTH, curAttr.fg, curAttr.bg);
|
||||
}
|
||||
}
|
||||
|
||||
// write current char to buffer and advance cursor
|
||||
bufferRow.set(buffer.x++, [curAttr, char, chWidth, code]);
|
||||
bufferRow.setCellFromCodePoint(buffer.x++, code, chWidth, curAttr.fg, curAttr.bg);
|
||||
|
||||
// fullwidth char - also set next cell to placeholder stub and advance cursor
|
||||
// for graphemes bigger than fullwidth we can simply loop to zero
|
||||
// we already made sure above, that buffer.x + chWidth will not overflow right
|
||||
if (chWidth > 0) {
|
||||
while (--chWidth) {
|
||||
bufferRow.set(buffer.x++, [curAttr, '', 0, undefined]);
|
||||
// other than a regular empty cell a cell following a wide char has no width
|
||||
bufferRow.setCellFromCodePoint(buffer.x++, 0, 0, curAttr.fg, curAttr.bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -534,7 +518,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).insertCells(
|
||||
this._terminal.buffer.x,
|
||||
params[0] || 1,
|
||||
[this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]
|
||||
this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())
|
||||
);
|
||||
this._terminal.updateRange(this._terminal.buffer.y);
|
||||
}
|
||||
@@ -708,7 +692,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
line.replaceCells(
|
||||
start,
|
||||
end,
|
||||
[this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]
|
||||
this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())
|
||||
);
|
||||
if (clearWrap) {
|
||||
line.isWrapped = false;
|
||||
@@ -831,7 +815,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// test: echo -e '\e[44m\e[1L\e[0m'
|
||||
// blankLine(true) - xterm/linux behavior
|
||||
buffer.lines.splice(scrollBottomAbsolute - 1, 1);
|
||||
buffer.lines.splice(row, 0, buffer.getBlankLine(this._terminal.eraseAttr()));
|
||||
buffer.lines.splice(row, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
|
||||
}
|
||||
|
||||
// this.maxRange();
|
||||
@@ -861,7 +845,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// test: echo -e '\e[44m\e[1M\e[0m'
|
||||
// blankLine(true) - xterm/linux behavior
|
||||
buffer.lines.splice(row, 1);
|
||||
buffer.lines.splice(j, 0, buffer.getBlankLine(this._terminal.eraseAttr()));
|
||||
buffer.lines.splice(j, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
|
||||
}
|
||||
|
||||
// this.maxRange();
|
||||
@@ -877,7 +861,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).deleteCells(
|
||||
this._terminal.buffer.x,
|
||||
params[0] || 1,
|
||||
[this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]
|
||||
this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())
|
||||
);
|
||||
this._terminal.updateRange(this._terminal.buffer.y);
|
||||
}
|
||||
@@ -893,7 +877,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
while (param--) {
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1);
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR));
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||
}
|
||||
// this.maxRange();
|
||||
this._terminal.updateRange(buffer.scrollTop);
|
||||
@@ -912,7 +896,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
while (param--) {
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR));
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||
}
|
||||
// this.maxRange();
|
||||
this._terminal.updateRange(buffer.scrollTop);
|
||||
@@ -928,7 +912,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).replaceCells(
|
||||
this._terminal.buffer.x,
|
||||
this._terminal.buffer.x + (params[0] || 1),
|
||||
[this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]
|
||||
this._terminal.buffer.getNullCell(this._terminal.eraseAttrData())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -984,9 +968,10 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// make buffer local for faster access
|
||||
const buffer = this._terminal.buffer;
|
||||
const line = buffer.lines.get(buffer.ybase + buffer.y);
|
||||
line.loadCell(buffer.x - 1, this._workCell);
|
||||
line.replaceCells(buffer.x,
|
||||
buffer.x + (params[0] || 1),
|
||||
line.get(buffer.x - 1) || [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]
|
||||
(this._workCell.content !== undefined) ? this._workCell : buffer.getNullCell(DEFAULT_ATTR_DATA)
|
||||
);
|
||||
// FIXME: no updateRange here?
|
||||
}
|
||||
@@ -1324,7 +1309,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// FALL-THROUGH
|
||||
case 47: // alt screen buffer
|
||||
case 1047: // alt screen buffer
|
||||
this._terminal.buffers.activateAltBuffer(this._terminal.eraseAttr());
|
||||
this._terminal.buffers.activateAltBuffer(this._terminal.eraseAttrData());
|
||||
this._terminal.refresh(0, this._terminal.rows - 1);
|
||||
if (this._terminal.viewport) {
|
||||
this._terminal.viewport.syncScrollArea();
|
||||
@@ -1588,127 +1573,124 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
public charAttributes(params: number[]): void {
|
||||
// Optimize a single SGR0.
|
||||
if (params.length === 1 && params[0] === 0) {
|
||||
this._terminal.curAttr = DEFAULT_ATTR;
|
||||
this._terminal.curAttrData.fg = DEFAULT_ATTR_DATA.fg;
|
||||
this._terminal.curAttrData.bg = DEFAULT_ATTR_DATA.bg;
|
||||
return;
|
||||
}
|
||||
|
||||
const l = params.length;
|
||||
let flags = this._terminal.curAttr >> 18;
|
||||
let fg = (this._terminal.curAttr >> 9) & 0x1ff;
|
||||
let bg = this._terminal.curAttr & 0x1ff;
|
||||
let p;
|
||||
const attr = this._terminal.curAttrData;
|
||||
|
||||
for (let i = 0; i < l; i++) {
|
||||
p = params[i];
|
||||
if (p >= 30 && p <= 37) {
|
||||
// fg color 8
|
||||
fg = p - 30;
|
||||
attr.fg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
|
||||
attr.fg |= Attributes.CM_P16 | (p - 30);
|
||||
} else if (p >= 40 && p <= 47) {
|
||||
// bg color 8
|
||||
bg = p - 40;
|
||||
attr.bg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
|
||||
attr.bg |= Attributes.CM_P16 | (p - 40);
|
||||
} else if (p >= 90 && p <= 97) {
|
||||
// fg color 16
|
||||
p += 8;
|
||||
fg = p - 90;
|
||||
attr.fg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
|
||||
attr.fg |= Attributes.CM_P16 | (p - 90) | 8;
|
||||
} else if (p >= 100 && p <= 107) {
|
||||
// bg color 16
|
||||
p += 8;
|
||||
bg = p - 100;
|
||||
attr.bg &= ~(Attributes.CM_MASK | Attributes.PCOLOR_MASK);
|
||||
attr.bg |= Attributes.CM_P16 | (p - 100) | 8;
|
||||
} else if (p === 0) {
|
||||
// default
|
||||
flags = DEFAULT_ATTR >> 18;
|
||||
fg = (DEFAULT_ATTR >> 9) & 0x1ff;
|
||||
bg = DEFAULT_ATTR & 0x1ff;
|
||||
// flags = 0;
|
||||
// fg = 0x1ff;
|
||||
// bg = 0x1ff;
|
||||
attr.fg = DEFAULT_ATTR_DATA.fg;
|
||||
attr.bg = DEFAULT_ATTR_DATA.bg;
|
||||
} else if (p === 1) {
|
||||
// bold text
|
||||
flags |= FLAGS.BOLD;
|
||||
attr.fg |= FgFlags.BOLD;
|
||||
} else if (p === 3) {
|
||||
// italic text
|
||||
flags |= FLAGS.ITALIC;
|
||||
attr.bg |= BgFlags.ITALIC;
|
||||
} else if (p === 4) {
|
||||
// underlined text
|
||||
flags |= FLAGS.UNDERLINE;
|
||||
attr.fg |= FgFlags.UNDERLINE;
|
||||
} else if (p === 5) {
|
||||
// blink
|
||||
flags |= FLAGS.BLINK;
|
||||
attr.fg |= FgFlags.BLINK;
|
||||
} else if (p === 7) {
|
||||
// inverse and positive
|
||||
// test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m'
|
||||
flags |= FLAGS.INVERSE;
|
||||
attr.fg |= FgFlags.INVERSE;
|
||||
} else if (p === 8) {
|
||||
// invisible
|
||||
flags |= FLAGS.INVISIBLE;
|
||||
attr.fg |= FgFlags.INVISIBLE;
|
||||
} else if (p === 2) {
|
||||
// dimmed text
|
||||
flags |= FLAGS.DIM;
|
||||
attr.bg |= BgFlags.DIM;
|
||||
} else if (p === 22) {
|
||||
// not bold nor faint
|
||||
flags &= ~FLAGS.BOLD;
|
||||
flags &= ~FLAGS.DIM;
|
||||
attr.fg &= ~FgFlags.BOLD;
|
||||
attr.bg &= ~BgFlags.DIM;
|
||||
} else if (p === 23) {
|
||||
// not italic
|
||||
flags &= ~FLAGS.ITALIC;
|
||||
attr.bg &= ~BgFlags.ITALIC;
|
||||
} else if (p === 24) {
|
||||
// not underlined
|
||||
flags &= ~FLAGS.UNDERLINE;
|
||||
attr.fg &= ~FgFlags.UNDERLINE;
|
||||
} else if (p === 25) {
|
||||
// not blink
|
||||
flags &= ~FLAGS.BLINK;
|
||||
attr.fg &= ~FgFlags.BLINK;
|
||||
} else if (p === 27) {
|
||||
// not inverse
|
||||
flags &= ~FLAGS.INVERSE;
|
||||
attr.fg &= ~FgFlags.INVERSE;
|
||||
} else if (p === 28) {
|
||||
// not invisible
|
||||
flags &= ~FLAGS.INVISIBLE;
|
||||
attr.fg &= ~FgFlags.INVISIBLE;
|
||||
} else if (p === 39) {
|
||||
// reset fg
|
||||
fg = (DEFAULT_ATTR >> 9) & 0x1ff;
|
||||
attr.fg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
|
||||
attr.fg |= DEFAULT_ATTR_DATA.fg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
|
||||
} else if (p === 49) {
|
||||
// reset bg
|
||||
bg = DEFAULT_ATTR & 0x1ff;
|
||||
attr.bg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
|
||||
attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
|
||||
} else if (p === 38) {
|
||||
// fg color 256
|
||||
if (params[i + 1] === 2) {
|
||||
i += 2;
|
||||
fg = this._terminal.matchColor(
|
||||
params[i] & 0xff,
|
||||
params[i + 1] & 0xff,
|
||||
params[i + 2] & 0xff);
|
||||
if (fg === -1) fg = 0x1ff;
|
||||
attr.fg |= Attributes.CM_RGB;
|
||||
attr.fg &= ~Attributes.RGB_MASK;
|
||||
attr.fg |= AttributeData.fromColorRGB([params[i], params[i + 1], params[i + 2]]);
|
||||
i += 2;
|
||||
} else if (params[i + 1] === 5) {
|
||||
i += 2;
|
||||
p = params[i] & 0xff;
|
||||
fg = p;
|
||||
attr.fg &= ~Attributes.PCOLOR_MASK;
|
||||
attr.fg |= Attributes.CM_P256 | p;
|
||||
}
|
||||
} else if (p === 48) {
|
||||
// bg color 256
|
||||
if (params[i + 1] === 2) {
|
||||
i += 2;
|
||||
bg = this._terminal.matchColor(
|
||||
params[i] & 0xff,
|
||||
params[i + 1] & 0xff,
|
||||
params[i + 2] & 0xff);
|
||||
if (bg === -1) bg = 0x1ff;
|
||||
attr.bg |= Attributes.CM_RGB;
|
||||
attr.bg &= ~Attributes.RGB_MASK;
|
||||
attr.bg |= AttributeData.fromColorRGB([params[i], params[i + 1], params[i + 2]]);
|
||||
i += 2;
|
||||
} else if (params[i + 1] === 5) {
|
||||
i += 2;
|
||||
p = params[i] & 0xff;
|
||||
bg = p;
|
||||
attr.bg &= ~Attributes.PCOLOR_MASK;
|
||||
attr.bg |= Attributes.CM_P256 | p;
|
||||
}
|
||||
} else if (p === 100) {
|
||||
// reset fg/bg
|
||||
fg = (DEFAULT_ATTR >> 9) & 0x1ff;
|
||||
bg = DEFAULT_ATTR & 0x1ff;
|
||||
attr.fg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
|
||||
attr.fg |= DEFAULT_ATTR_DATA.fg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
|
||||
attr.bg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
|
||||
attr.bg |= DEFAULT_ATTR_DATA.bg & (Attributes.PCOLOR_MASK | Attributes.RGB_MASK);
|
||||
} else {
|
||||
this._terminal.error('Unknown SGR attribute: %d.', p);
|
||||
}
|
||||
}
|
||||
|
||||
this._terminal.curAttr = (flags << 18) | (fg << 9) | bg;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1795,7 +1777,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._terminal.applicationCursor = false;
|
||||
this._terminal.buffer.scrollTop = 0;
|
||||
this._terminal.buffer.scrollBottom = this._terminal.rows - 1;
|
||||
this._terminal.curAttr = DEFAULT_ATTR;
|
||||
this._terminal.curAttrData = DEFAULT_ATTR_DATA;
|
||||
this._terminal.buffer.x = this._terminal.buffer.y = 0; // ?
|
||||
this._terminal.charset = null;
|
||||
this._terminal.glevel = 0; // ??
|
||||
@@ -1858,7 +1840,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
public saveCursor(params: number[]): void {
|
||||
this._terminal.buffer.savedX = this._terminal.buffer.x;
|
||||
this._terminal.buffer.savedY = this._terminal.buffer.y;
|
||||
this._terminal.buffer.savedCurAttr = this._terminal.curAttr;
|
||||
this._terminal.buffer.savedCurAttrData.fg = this._terminal.curAttrData.fg;
|
||||
this._terminal.buffer.savedCurAttrData.bg = this._terminal.curAttrData.bg;
|
||||
}
|
||||
|
||||
|
||||
@@ -1870,7 +1853,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
public restoreCursor(params: number[]): void {
|
||||
this._terminal.buffer.x = this._terminal.buffer.savedX || 0;
|
||||
this._terminal.buffer.y = this._terminal.buffer.savedY || 0;
|
||||
this._terminal.curAttr = this._terminal.buffer.savedCurAttr || DEFAULT_ATTR;
|
||||
this._terminal.curAttrData.fg = this._terminal.buffer.savedCurAttrData.fg;
|
||||
this._terminal.curAttrData.bg = this._terminal.buffer.savedCurAttrData.bg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user