Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b29b8a9c6 | ||
|
|
c994c67dd9 | ||
|
|
28bcd88fb0 | ||
|
|
bee4b0ff06 | ||
|
|
559b3c1392 | ||
|
|
d3c9f70bec | ||
|
|
ff74caf50d | ||
|
|
3d941f2fdf | ||
|
|
93aab95f7b | ||
|
|
db8e449a8a | ||
|
|
4da5d557a5 | ||
|
|
a1dc85ee57 | ||
|
|
554b4a8224 | ||
|
|
3f659a15da | ||
|
|
dd5b295219 | ||
|
|
9d0200252b | ||
|
|
d67faff92a | ||
|
|
c50c134e25 | ||
|
|
74ea558bfc | ||
|
|
c6f93701f5 | ||
|
|
8a4772ca39 | ||
|
|
66c80d8b78 | ||
|
|
0cb38cfdcb | ||
|
|
c669c3fba7 | ||
|
|
8860726c68 | ||
|
|
f64724f54f | ||
|
|
fd222b58b5 | ||
|
|
83ba481e3d | ||
|
|
7d5bbc341f | ||
|
|
519e44d30e | ||
|
|
3bb2c158fd | ||
|
|
5ada5f0c0c | ||
|
|
ea1fcc6a5f | ||
|
|
19c14b076b | ||
|
|
676aabc3ed | ||
|
|
5c33e4008d | ||
|
|
772e446192 | ||
|
|
66be7797cf | ||
|
|
3261c53c08 | ||
|
|
5bab94730f | ||
|
|
801aeb8462 | ||
|
|
cbe5abcf27 | ||
|
|
72dbd43be6 | ||
|
|
7145130835 | ||
|
|
b34289cc44 | ||
|
|
fe2b1c0d43 | ||
|
|
4de6f1c255 | ||
|
|
8baff9bade | ||
|
|
f8f7e7e4c1 | ||
|
|
7523505af1 | ||
|
|
d94b119e9d | ||
|
|
3cab8511e4 | ||
|
|
7d3f5375a8 | ||
|
|
b2c4ed638b | ||
|
|
2804bfa8b7 | ||
|
|
64ac67026a | ||
|
|
5a74ce2e59 | ||
|
|
f4a56389cd | ||
|
|
52b64beefc | ||
|
|
ec1234e8d8 | ||
|
|
b7695d2fd6 | ||
|
|
4c6dc45d20 | ||
|
|
5cb048094e | ||
|
|
e3cccd619b | ||
|
|
88f0b91056 | ||
|
|
78ddb9feba | ||
|
|
f68ff2a455 | ||
|
|
04e2bdd6ec | ||
|
|
84fa6be370 | ||
|
|
af2f744186 | ||
|
|
5f4b719444 | ||
|
|
9616851e52 | ||
|
|
4645fda126 | ||
|
|
057a8f6f1b | ||
|
|
e6917d1933 | ||
|
|
6535995753 | ||
|
|
77ed5ba7ed | ||
|
|
bc86b7804c | ||
|
|
d5f4ae53c9 | ||
|
|
b898e016f3 | ||
|
|
712f0ae2a7 | ||
|
|
c553107d23 | ||
|
|
b127436cc4 | ||
|
|
b61ac11a76 | ||
|
|
b22d7f1140 | ||
|
|
925aaf18a1 | ||
|
|
797ed47791 | ||
|
|
4128a6e2f1 | ||
|
|
69f34be4b8 | ||
|
|
1c3da7e2e6 | ||
|
|
5c7644adc0 | ||
|
|
f974010aa3 | ||
|
|
d4d18f741f | ||
|
|
d97b54b9be | ||
|
|
a7b94fbe1a | ||
|
|
2ab9de91fe | ||
|
|
fd6bf6d667 | ||
|
|
38e81f053f | ||
|
|
c6f1b456f5 | ||
|
|
8484978ef0 | ||
|
|
c4919b9a6b | ||
|
|
02e4db4c82 | ||
|
|
8d9badb588 | ||
|
|
fc534a1067 | ||
|
|
f68ef19868 | ||
|
|
0a2e40502f | ||
|
|
e843a2f61e | ||
|
|
d95a633f16 | ||
|
|
6f41aad2ef | ||
|
|
e76cbd9c3f | ||
|
|
0c8b299996 | ||
|
|
86420d2535 | ||
|
|
9e4dcdaf91 | ||
|
|
45e7bf274a | ||
|
|
62cf413d54 | ||
|
|
3e0a270891 | ||
|
|
22d79eef26 | ||
|
|
31d806afce | ||
|
|
1e9ca83120 | ||
|
|
3d9a33bb69 | ||
|
|
ea989ed691 | ||
|
|
8e151ee6ef | ||
|
|
dc904b27a2 | ||
|
|
4de539a1ff | ||
|
|
913de3158e | ||
|
|
049ca653ed | ||
|
|
246d137c2c | ||
|
|
99b1a7a49b | ||
|
|
9fa068f574 | ||
|
|
91337d7e57 | ||
|
|
cb555d5297 | ||
|
|
50dd3cb841 | ||
|
|
b7750cbf91 | ||
|
|
dde47269de | ||
|
|
bc3f2a12c0 | ||
|
|
bdb112b23c | ||
|
|
214ccd14ac | ||
|
|
e5f3820285 | ||
|
|
f75b4cfa93 | ||
|
|
f5d651dda1 | ||
|
|
a90254e778 | ||
|
|
a6bcfcfba4 | ||
|
|
78ae1d09f7 | ||
|
|
1132da8563 | ||
|
|
199b9ceb6d | ||
|
|
8e41d22f8d | ||
|
|
57bd61697c | ||
|
|
3e8b8dc6ef | ||
|
|
b4bc830cee | ||
|
|
2e43f615f6 | ||
|
|
5f2d1d2b4e | ||
|
|
4ee305fa21 | ||
|
|
4b3e16dbd0 | ||
|
|
a7c836e42c | ||
|
|
886c057d0f | ||
|
|
038704786d | ||
|
|
c61076830f | ||
|
|
02fccfbdc7 | ||
|
|
b250cedf81 | ||
|
|
7243e330fc | ||
|
|
17a7fb2a6e | ||
|
|
d3d8ec3b20 | ||
|
|
b6074df4bf | ||
|
|
f3cc9469a7 | ||
|
|
a9d2b8a55c | ||
|
|
73e235d9f8 | ||
|
|
17ec85e39e | ||
|
|
723c64e481 | ||
|
|
faa60be6fb | ||
|
|
e95f9815d8 | ||
|
|
9b62d8a01a | ||
|
|
1c206c2c27 | ||
|
|
782797ca11 | ||
|
|
b34ee5157e | ||
|
|
ae37c43cd4 | ||
|
|
272b1d2194 | ||
|
|
260e2dab16 | ||
|
|
7c78390e2b | ||
|
|
f5a4eed27c | ||
|
|
b0768f18ca | ||
|
|
4d1e532069 | ||
|
|
5beb7f52c2 | ||
|
|
eaaf9b7af1 | ||
|
|
3c9687decf | ||
|
|
a5e269a3f9 | ||
|
|
30d84b1202 | ||
|
|
0aa1079cd0 | ||
|
|
1091255d2c | ||
|
|
56e63d762b | ||
|
|
f3c8b8f395 | ||
|
|
1a0a56c1f2 | ||
|
|
055267ee9c | ||
|
|
6e9040e6ba | ||
|
|
a32776e2af | ||
|
|
1d6d6eb716 | ||
|
|
003cf96910 | ||
|
|
3cb87af315 | ||
|
|
e32d8774eb | ||
|
|
49d5608fa6 | ||
|
|
56bfdb64d4 | ||
|
|
9513c231c9 | ||
|
|
dd92161da5 | ||
|
|
55571a1ae8 | ||
|
|
c112990d8f | ||
|
|
23dba6aebd | ||
|
|
f669b8ed1d | ||
|
|
9a22cbe44b | ||
|
|
883ddba934 | ||
|
|
8f7295b8a2 | ||
|
|
0ba97f6941 | ||
|
|
d979c077a0 | ||
|
|
4e18a78c02 | ||
|
|
1d6e8abcca | ||
|
|
82a65be446 | ||
|
|
0778a823d1 | ||
|
|
eca36458b3 |
+11
-4
@@ -12,14 +12,21 @@
|
||||
"test/api/tsconfig.json",
|
||||
"test/benchmark/tsconfig.json",
|
||||
"addons/xterm-addon-attach/src/tsconfig.json",
|
||||
"addons/xterm-addon-attach/test/tsconfig.json",
|
||||
"addons/xterm-addon-fit/src/tsconfig.json",
|
||||
"addons/xterm-addon-fit/test/tsconfig.json",
|
||||
"addons/xterm-addon-ligatures/src/tsconfig.json",
|
||||
"addons/xterm-addon-search/src/tsconfig.json",
|
||||
"addons/xterm-addon-unicode11/src/tsconfig.json",
|
||||
"addons/xterm-addon-web-links/src/tsconfig.json",
|
||||
"addons/xterm-addon-webgl/src/tsconfig.json",
|
||||
"addons/xterm-addon-search/test/tsconfig.json",
|
||||
"addons/xterm-addon-serialize/src/tsconfig.json",
|
||||
"addons/xterm-addon-serialize/test/tsconfig.json",
|
||||
"addons/xterm-addon-serialize/benchmark/tsconfig.json",
|
||||
"addons/xterm-addon-ligatures/src/tsconfig.json"
|
||||
"addons/xterm-addon-unicode11/src/tsconfig.json",
|
||||
"addons/xterm-addon-unicode11/test/tsconfig.json",
|
||||
"addons/xterm-addon-web-links/src/tsconfig.json",
|
||||
"addons/xterm-addon-web-links/test/tsconfig.json",
|
||||
"addons/xterm-addon-webgl/src/tsconfig.json",
|
||||
"addons/xterm-addon-webgl/test/tsconfig.json"
|
||||
],
|
||||
"sourceType": "module"
|
||||
},
|
||||
|
||||
@@ -87,7 +87,7 @@ The xterm.js team maintains the following addons but they can be built by anyone
|
||||
|
||||
Since xterm.js is typically implemented as a developer tool, only modern browsers are supported officially. Specifically the latest versions of *Chrome*, *Edge*, *Firefox* and *Safari*.
|
||||
|
||||
We also partially support *Intenet Explorer 11*, meaning xterm.js should work for the most part, but we reserve the right to not provide workarounds specifically for it unless it's absolutely necessary to get the basic input/output flow working.
|
||||
We also partially support *Internet Explorer 11*, meaning xterm.js should work for the most part, but we reserve the right to not provide workarounds specifically for it unless it's absolutely necessary to get the basic input/output flow working.
|
||||
|
||||
Xterm.js works seamlessly in [Electron](https://electronjs.org/) apps and may even work on earlier versions of the browsers, these are the versions we strive to keep working.
|
||||
|
||||
@@ -168,6 +168,9 @@ Xterm.js is used in several world-class applications to provide great terminal e
|
||||
- [**Repl.it**](https://repl.it): Collaborative browser based IDE with support for 50+ different languages.
|
||||
- [**TeleType**](https://github.com/akshaykmr/TeleType): cli tool that allows you to share your terminal online conveniently. Show off mad cli-fu, help a colleague, teach, or troubleshoot.
|
||||
- [**Intervue**](https://www.intervue.io): Pair programming for interviews. Multiple programming languages supported, with results displayed by xterm.js.
|
||||
- [**TRASA**](https://trasa.io): Zero trust access to Web, SSH, RDP and Database services.
|
||||
- [**Commas**](https://github.com/CyanSalt/commas): Commas is a hackable terminal and command runner.
|
||||
- [**Devtron**](https://github.com/devtron-labs/devtron): Software Delivery Workflow For Kubernetes.
|
||||
[And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
|
||||
|
||||
Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list. Note: Please add any new contributions to the end of the list only.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"repository": "https://github.com/xtermjs/xterm.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "../../node_modules/.bin/tsc -p src",
|
||||
"build": "../../node_modules/.bin/tsc -p .",
|
||||
"prepackage": "npm run build",
|
||||
"package": "../../node_modules/.bin/webpack",
|
||||
"prepublishOnly": "npm run package"
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
"removeComments": true,
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../out-test/api/TestUtils"
|
||||
"../../../node_modules/@types/mocha"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
import WebSocket = require('ws');
|
||||
import { openTerminal, pollFor, getBrowserType } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright-core';
|
||||
import { Browser, Page } from 'playwright';
|
||||
|
||||
const APP = 'http://127.0.0.1:3000/test';
|
||||
|
||||
@@ -17,7 +17,7 @@ const height = 600;
|
||||
describe('AttachAddon', () => {
|
||||
before(async function(): Promise<any> {
|
||||
const browserType = getBrowserType();
|
||||
browser = await browserType.launch({ dumpio: true,
|
||||
browser = await browserType.launch({
|
||||
headless: process.argv.indexOf('--headless') !== -1
|
||||
});
|
||||
page = await (await browser.newContext()).newPage();
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es2015"
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out-test",
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../node_modules/@types/node",
|
||||
"../../../out-test/api/TestUtils"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../../../typings/xterm.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{ "path": "./src" },
|
||||
{ "path": "./test" }
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-fit",
|
||||
"version": "0.4.0",
|
||||
"version": "0.5.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
@@ -10,7 +10,7 @@
|
||||
"repository": "https://github.com/xtermjs/xterm.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "../../node_modules/.bin/tsc -p src",
|
||||
"build": "../../node_modules/.bin/tsc -p .",
|
||||
"prepackage": "npm run build",
|
||||
"package": "../../node_modules/.bin/webpack",
|
||||
"prepublishOnly": "npm run package"
|
||||
|
||||
@@ -59,6 +59,10 @@ export class FitAddon implements ITerminalAddon {
|
||||
// TODO: Remove reliance on private API
|
||||
const core = (this._terminal as any)._core;
|
||||
|
||||
if (core._renderService.dimensions.actualCellWidth === 0 || core._renderService.dimensions.actualCellHeight === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const parentElementStyle = window.getComputedStyle(this._terminal.element.parentElement);
|
||||
const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));
|
||||
const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')));
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
"removeComments": true,
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../out-test/api/TestUtils"
|
||||
"../../../node_modules/@types/mocha"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
|
||||
+19
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { openTerminal, getBrowserType } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright-core';
|
||||
import { Browser, Page } from 'playwright';
|
||||
|
||||
const APP = 'http://127.0.0.1:3000/test';
|
||||
|
||||
@@ -17,12 +17,16 @@ const height = 768;
|
||||
describe('FitAddon', () => {
|
||||
before(async function(): Promise<any> {
|
||||
const browserType = getBrowserType();
|
||||
browser = await browserType.launch({ dumpio: true,
|
||||
browser = await browserType.launch({
|
||||
headless: process.argv.indexOf('--headless') !== -1
|
||||
});
|
||||
page = await (await browser.newContext()).newPage();
|
||||
await page.setViewportSize({ width, height });
|
||||
await page.goto(APP);
|
||||
});
|
||||
|
||||
beforeEach(async function(): Promise<any> {
|
||||
await page.evaluate(`document.querySelector('#terminal-container').style.display=''`);
|
||||
await openTerminal(page);
|
||||
});
|
||||
|
||||
@@ -30,6 +34,10 @@ describe('FitAddon', () => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
afterEach(async function(): Promise<any> {
|
||||
await page.evaluate(`window.term.dispose()`);
|
||||
});
|
||||
|
||||
it('no terminal', async function(): Promise<any> {
|
||||
await page.evaluate(`window.fit = new FitAddon();`);
|
||||
assert.equal(await page.evaluate(`window.fit.proposeDimensions()`), undefined);
|
||||
@@ -63,6 +71,15 @@ describe('FitAddon', () => {
|
||||
rows: 1
|
||||
});
|
||||
});
|
||||
|
||||
it('hidden', async function(): Promise<any> {
|
||||
await page.evaluate(`window.term.dispose()`);
|
||||
await page.evaluate(`document.querySelector('#terminal-container').style.display='none'`);
|
||||
await page.evaluate(`window.term = new Terminal()`);
|
||||
await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);
|
||||
await loadFit();
|
||||
assert.equal(await page.evaluate(`window.fit.proposeDimensions()`), undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fit', () => {
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es2015"
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out-test",
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../node_modules/@types/node",
|
||||
"../../../out-test/api/TestUtils"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../../../typings/xterm.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{ "path": "./src" },
|
||||
{ "path": "./test" }
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-ligatures",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"description": "Add support for programming ligatures to xterm.js",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
@@ -31,7 +31,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"font-finder": "^1.0.4",
|
||||
"font-finder": "^1.1.0",
|
||||
"font-ligatures": "^1.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"module": "commonjs",
|
||||
"sourceMap": true,
|
||||
"outDir": "../out",
|
||||
"rootDir": ".",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"preserveWatchOutput": true
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../../../typings/xterm.d.ts"
|
||||
"compilerOptions": {
|
||||
"target": "es2017",
|
||||
"module": "commonjs",
|
||||
"sourceMap": true,
|
||||
"outDir": "../out",
|
||||
"rootDir": ".",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"preserveWatchOutput": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../../../typings/xterm.d.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{ "path": "./src" }
|
||||
]
|
||||
}
|
||||
@@ -78,13 +78,21 @@ follow-redirects@1.5.10:
|
||||
dependencies:
|
||||
debug "=3.1.0"
|
||||
|
||||
font-finder@^1.0.3, font-finder@^1.0.4:
|
||||
font-finder@^1.0.3:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/font-finder/-/font-finder-1.0.4.tgz#2ca944954dd8d0e1b5bdc4c596cc08607761d89b"
|
||||
dependencies:
|
||||
get-system-fonts "^2.0.0"
|
||||
promise-stream-reader "^1.0.1"
|
||||
|
||||
font-finder@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/font-finder/-/font-finder-1.1.0.tgz#2bff2b2762acba720239c8bec898a96daae90858"
|
||||
integrity sha512-wpCL2uIbi6GurJbU7ZlQ3nGd61Ho+dSU6U83/xJT5UPFfN35EeCW/rOtS+5k+IuEZu2SYmHzDIPL9eA5tSYRAw==
|
||||
dependencies:
|
||||
get-system-fonts "^2.0.0"
|
||||
promise-stream-reader "^1.0.1"
|
||||
|
||||
font-ligatures@^1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/font-ligatures/-/font-ligatures-1.3.3.tgz#63fff18dc8adb3a11fe5eec1f4e8d7edfa8075b9"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-search",
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
@@ -10,7 +10,7 @@
|
||||
"repository": "https://github.com/xtermjs/xterm.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "../../node_modules/.bin/tsc -p src",
|
||||
"build": "../../node_modules/.bin/tsc -p .",
|
||||
"prepackage": "npm run build",
|
||||
"package": "../../node_modules/.bin/webpack",
|
||||
"prepublishOnly": "npm run package"
|
||||
|
||||
@@ -158,8 +158,15 @@ export class SearchAddon implements ITerminalAddon {
|
||||
};
|
||||
|
||||
if (incremental) {
|
||||
// Try to expand selection to right first.
|
||||
result = this._findInLine(term, searchPosition, searchOptions, false);
|
||||
if (!(result && result.row === startRow && result.col === startCol)) {
|
||||
const isOldResultHighlighted = result && result.row === startRow && result.col === startCol;
|
||||
if (!isOldResultHighlighted) {
|
||||
// If selection was not able to be expanded to the right, then try reverse search
|
||||
if (currentSelection) {
|
||||
searchPosition.startRow = currentSelection.endRow;
|
||||
searchPosition.startCol = currentSelection.endColumn;
|
||||
}
|
||||
result = this._findInLine(term, searchPosition, searchOptions, true);
|
||||
}
|
||||
} else {
|
||||
@@ -245,6 +252,7 @@ export class SearchAddon implements ITerminalAddon {
|
||||
* @param term The search term.
|
||||
* @param position The position to start the search.
|
||||
* @param searchOptions Search options.
|
||||
* @param isReverseSearch Whether the search should start from the right side of the terminal and search to the left.
|
||||
* @return The search result if it was found.
|
||||
*/
|
||||
protected _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}, isReverseSearch: boolean = false): ISearchResult | undefined {
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
"removeComments": true,
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../out-test/api/TestUtils"
|
||||
"../../../node_modules/@types/mocha"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
|
||||
+11
-4
@@ -7,7 +7,7 @@ import { assert } from 'chai';
|
||||
import { readFile } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { openTerminal, writeSync, getBrowserType } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright-core';
|
||||
import { Browser, Page } from 'playwright';
|
||||
|
||||
const APP = 'http://127.0.0.1:3000/test';
|
||||
|
||||
@@ -19,7 +19,7 @@ const height = 600;
|
||||
describe('Search Tests', function(): void {
|
||||
before(async function(): Promise<any> {
|
||||
const browserType = getBrowserType();
|
||||
browser = await browserType.launch({ dumpio: true,
|
||||
browser = await browserType.launch({
|
||||
headless: process.argv.indexOf('--headless') !== -1
|
||||
});
|
||||
page = await (await browser.newContext()).newPage();
|
||||
@@ -111,8 +111,15 @@ describe('Search Tests', function(): void {
|
||||
let fixture: string;
|
||||
before(async () => {
|
||||
const rawFixture = await new Promise<Buffer>(r => readFile(resolve(__dirname, '../fixtures/issue-2444'), (err, data) => r(data)));
|
||||
fixture = rawFixture.toString()
|
||||
.replace(/\n/g, '\\n\\r')
|
||||
if (process.platform === 'win32') {
|
||||
fixture = rawFixture.toString()
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/\r/g, '\\r');
|
||||
} else {
|
||||
fixture = rawFixture.toString()
|
||||
.replace(/\n/g, '\\n\\r');
|
||||
}
|
||||
fixture = fixture
|
||||
.replace(/'/g, '\\\'');
|
||||
});
|
||||
it('should find all occurrences using findNext', async () => {
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es6",
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out-test",
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../node_modules/@types/node",
|
||||
"../../../out-test/api/TestUtils"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../../../typings/xterm.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{ "path": "./src" },
|
||||
{ "path": "./test" }
|
||||
]
|
||||
}
|
||||
@@ -16,12 +16,12 @@ class TestTerminal extends Terminal {
|
||||
}
|
||||
}
|
||||
|
||||
perfContext('Terminal: sh -c "dd if=/dev/random count=40 bs=1k | hexdump | lolcat -f"', () => {
|
||||
perfContext('Terminal: sh -c "dd if=/dev/urandom count=40 bs=1k | hexdump | lolcat -f"', () => {
|
||||
let content = '';
|
||||
let contentUtf8: Uint8Array;
|
||||
|
||||
before(async () => {
|
||||
const p = spawn('sh', ['-c', 'dd if=/dev/random count=40 bs=1k | hexdump | lolcat -f'], {
|
||||
const p = spawn('sh', ['-c', 'dd if=/dev/urandom count=40 bs=1k | hexdump | lolcat -f'], {
|
||||
name: 'xterm-256color',
|
||||
cols: 80,
|
||||
rows: 25,
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
"types": "typings/xterm-addon-serialize.d.ts",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "../../node_modules/.bin/tsc -p src",
|
||||
"build": "../../node_modules/.bin/tsc -p .",
|
||||
"prepackage": "npm run build",
|
||||
"package": "../../node_modules/.bin/webpack",
|
||||
"prepublishOnly": "npm run package",
|
||||
"benchmark": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json",
|
||||
"benchmark-baseline": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline out-benchmark/addons/xterm-addon-serialize/benchmark/*benchmark.js",
|
||||
"benchmark-eval": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/addons/xterm-addon-serialize/benchmark/*benchmark.js"
|
||||
"benchmark-baseline": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline out-benchmark/benchmark/*benchmark.js",
|
||||
"benchmark-eval": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/benchmark/*benchmark.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"xterm": "^4.0.0"
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
},
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../out-test/api/TestUtils"
|
||||
"../../../node_modules/@types/mocha"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { openTerminal, writeSync, getBrowserType } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright-core';
|
||||
import { Browser, Page } from 'playwright';
|
||||
|
||||
const APP = 'http://127.0.0.1:3000/test';
|
||||
|
||||
@@ -17,7 +17,7 @@ const height = 600;
|
||||
describe('SerializeAddon', () => {
|
||||
before(async function(): Promise<any> {
|
||||
const browserType = getBrowserType();
|
||||
browser = await browserType.launch({ dumpio: true,
|
||||
browser = await browserType.launch({
|
||||
headless: process.argv.indexOf('--headless') !== -1
|
||||
});
|
||||
page = await (await browser.newContext()).newPage();
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es2015"
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out-test",
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"common/*": [
|
||||
"../../../src/common/*"
|
||||
]
|
||||
},
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../node_modules/@types/node",
|
||||
"../../../out-test/api/TestUtils"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../../../typings/xterm.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../src/common"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{ "path": "./src" },
|
||||
{ "path": "./test" },
|
||||
{ "path": "./benchmark" }
|
||||
]
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
"repository": "https://github.com/xtermjs/xterm.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "../../node_modules/.bin/tsc -p src",
|
||||
"build": "../../node_modules/.bin/tsc -p .",
|
||||
"prepackage": "npm run build",
|
||||
"package": "../../node_modules/.bin/webpack",
|
||||
"prepublishOnly": "npm run package"
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
]
|
||||
},
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../out-test/api/TestUtils"
|
||||
"../../../node_modules/@types/mocha"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { openTerminal, getBrowserType } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright-core';
|
||||
import { Browser, Page } from 'playwright';
|
||||
|
||||
const APP = 'http://127.0.0.1:3000/test';
|
||||
|
||||
@@ -17,7 +17,7 @@ const height = 600;
|
||||
describe('Unicode11Addon', () => {
|
||||
before(async function(): Promise<any> {
|
||||
const browserType = getBrowserType();
|
||||
browser = await browserType.launch({ dumpio: true,
|
||||
browser = await browserType.launch({
|
||||
headless: process.argv.indexOf('--headless') !== -1
|
||||
});
|
||||
page = await (await browser.newContext()).newPage();
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015"
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out-test",
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"strict": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"common/*": [
|
||||
"../../../src/common/*"
|
||||
]
|
||||
},
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../node_modules/@types/node",
|
||||
"../../../out-test/api/TestUtils"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../../../typings/xterm.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../src/common"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{ "path": "./src" },
|
||||
{ "path": "./test" }
|
||||
]
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
"repository": "https://github.com/xtermjs/xterm.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "../../node_modules/.bin/tsc -p src",
|
||||
"build": "../../node_modules/.bin/tsc -p .",
|
||||
"prepackage": "npm run build",
|
||||
"package": "../../node_modules/.bin/webpack",
|
||||
"prepublishOnly": "npm run package"
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
"removeComments": true,
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../out-test/api/TestUtils"
|
||||
"../../../node_modules/@types/mocha"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { openTerminal, pollFor, writeSync, getBrowserType } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright-core';
|
||||
import { Browser, Page } from 'playwright';
|
||||
|
||||
const APP = 'http://127.0.0.1:3000/test';
|
||||
|
||||
@@ -17,7 +17,7 @@ const height = 600;
|
||||
describe('WebLinksAddon', () => {
|
||||
before(async function(): Promise<any> {
|
||||
const browserType = getBrowserType();
|
||||
browser = await browserType.launch({ dumpio: true,
|
||||
browser = await browserType.launch({
|
||||
headless: process.argv.indexOf('--headless') !== -1
|
||||
});
|
||||
page = await (await browser.newContext()).newPage();
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es2015"
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out-test",
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../node_modules/@types/node",
|
||||
"../../../out-test/api/TestUtils"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../../../typings/xterm.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{ "path": "./src" },
|
||||
{ "path": "./test" }
|
||||
]
|
||||
}
|
||||
@@ -3,3 +3,5 @@
|
||||
tsconfig.json
|
||||
.yarnrc
|
||||
webpack.config.js
|
||||
out-test/
|
||||
test/
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"repository": "https://github.com/xtermjs/xterm.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "../../node_modules/.bin/tsc -p src",
|
||||
"build": "../../node_modules/.bin/tsc -p .",
|
||||
"prepackage": "npm run build",
|
||||
"package": "../../node_modules/.bin/webpack",
|
||||
"prepublishOnly": "npm run package"
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
},
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../out-test/api/TestUtils"
|
||||
"../../../node_modules/@types/mocha"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ import { ITerminalOptions } from '../../../src/common/Types';
|
||||
import { ITheme } from 'xterm';
|
||||
import { assert } from 'chai';
|
||||
import { openTerminal, pollFor, writeSync, getBrowserType } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright-core';
|
||||
import { Browser, Page } from 'playwright';
|
||||
|
||||
const APP = 'http://127.0.0.1:3000/test';
|
||||
|
||||
@@ -892,7 +892,7 @@ async function getCellColor(col: number, row: number): Promise<number[]> {
|
||||
|
||||
async function setupBrowser(options: ITerminalOptions = { rendererType: 'dom' }): Promise<void> {
|
||||
const browserType = getBrowserType();
|
||||
browser = await browserType.launch({ dumpio: true,
|
||||
browser = await browserType.launch({
|
||||
headless: process.argv.indexOf('--headless') !== -1
|
||||
});
|
||||
page = await (await browser.newContext()).newPage();
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es6",
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out-test",
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"common/*": [
|
||||
"../../../src/common/*"
|
||||
],
|
||||
"browser/*": [
|
||||
"../../../src/browser/*"
|
||||
]
|
||||
},
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../node_modules/@types/node",
|
||||
"../../../out-test/api/TestUtils"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../../../typings/xterm.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../src/common"
|
||||
},
|
||||
{
|
||||
"path": "../../../src/browser"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{ "path": "./src" },
|
||||
{ "path": "./test" }
|
||||
]
|
||||
}
|
||||
@@ -98,11 +98,6 @@ jobs:
|
||||
inputs:
|
||||
versionSpec: '1.x'
|
||||
displayName: 'Install Yarn'
|
||||
- task: CacheBeta@1
|
||||
inputs:
|
||||
key: yarn2 | $(Agent.OS) | yarn.lock
|
||||
path: node_modules
|
||||
displayName: Cache node modules
|
||||
- script: yarn --frozen-lockfile
|
||||
displayName: 'Install dependencies and build'
|
||||
- script: |
|
||||
@@ -122,11 +117,6 @@ jobs:
|
||||
inputs:
|
||||
versionSpec: '10.x'
|
||||
displayName: 'Install Node.js'
|
||||
- task: CacheBeta@1
|
||||
inputs:
|
||||
key: yarn2 | $(Agent.OS) | yarn.lock
|
||||
path: node_modules
|
||||
displayName: Cache node modules
|
||||
- script: yarn --frozen-lockfile
|
||||
displayName: 'Install dependencies and build'
|
||||
- script: |
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const playwright = require('playwright-core');
|
||||
const fs = require('fs');
|
||||
|
||||
// Default to chromium
|
||||
let browserType = playwright['chromium'];
|
||||
const index = process.argv.indexOf('--browser');
|
||||
if (index !== -1 && process.argv.length > index + 1 && typeof process.argv[index + 1] === 'string') {
|
||||
const string = process.argv[index + 1];
|
||||
if (string === 'firefox' || string === 'webkit') {
|
||||
browserType = playwright[string];
|
||||
}
|
||||
}
|
||||
|
||||
const exists = fs.existsSync(browserType.executablePath());
|
||||
if (!exists) {
|
||||
console.log(`Downloading ${browserType.name()}`);
|
||||
browserType.downloadBrowserIfNeeded().then(() => process.exit(0));
|
||||
}
|
||||
@@ -359,7 +359,7 @@ function empty(ar) {
|
||||
}
|
||||
|
||||
function* parseMultiLineGen(filename, s) {
|
||||
if (!~s.indexOf('@vt:')) {
|
||||
if (!s.includes('@vt:')) {
|
||||
return;
|
||||
}
|
||||
const lines = s.split('\n').map(el => el.trim().replace(/[*]/, '').replace(/\s/, ''));
|
||||
@@ -413,7 +413,7 @@ function parseSingleLine(filename, s) {
|
||||
const line = s.trim();
|
||||
const match = line.match(REX_VT_LINE);
|
||||
if (match !== null) {
|
||||
if (!~TYPES.indexOf(match[2])) {
|
||||
if (!TYPES.includes(match[2])) {
|
||||
throw new Error(`unkown vt-command type "${match[2]}" specified in "${filename}"`);
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -25,7 +25,7 @@ if (fs.existsSync(addonsPath)) {
|
||||
|
||||
// walk all addon folders
|
||||
fs.readdir(addonsPath, (err, files) => {
|
||||
files.forEach(folder => {
|
||||
for (const folder of files) {
|
||||
const addonPath = path.join(addonsPath, folder);
|
||||
|
||||
// install only if there are dependencies listed
|
||||
@@ -51,6 +51,6 @@ if (fs.existsSync(addonsPath)) {
|
||||
} else {
|
||||
console.log('Skipped', folder);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+5
-5
@@ -11,7 +11,7 @@ const path = require('path');
|
||||
// Setup auth
|
||||
fs.writeFileSync(`${process.env['HOME']}/.npmrc`, `//registry.npmjs.org/:_authToken=${process.env['NPM_AUTH_TOKEN']}`);
|
||||
|
||||
const isDryRun = process.argv.indexOf('--dry') !== -1;
|
||||
const isDryRun = process.argv.includes('--dry');
|
||||
if (isDryRun) {
|
||||
console.log('Publish dry run');
|
||||
}
|
||||
@@ -36,13 +36,13 @@ const addonPackageDirs = [
|
||||
path.resolve(__dirname, '../addons/xterm-addon-webgl')
|
||||
];
|
||||
console.log(`Checking if addons need to be published`);
|
||||
addonPackageDirs.forEach(p => {
|
||||
for (const p of addonPackageDirs) {
|
||||
const addon = path.basename(p);
|
||||
if (changedFiles.some(e => e.indexOf(addon) !== -1)) {
|
||||
if (changedFiles.some(e => e.includes(addon))) {
|
||||
console.log(`Try publish ${addon}`);
|
||||
checkAndPublishPackage(p);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Publish website if it's a stable release
|
||||
if (isStableRelease) {
|
||||
@@ -54,7 +54,7 @@ function checkAndPublishPackage(packageDir) {
|
||||
|
||||
// Determine if this is a stable or beta release
|
||||
const publishedVersions = getPublishedVersions(packageJson);
|
||||
const isStableRelease = publishedVersions.indexOf(packageJson.version) === -1;
|
||||
const isStableRelease = !publishedVersions.includes(packageJson.version);
|
||||
|
||||
// Get the next version
|
||||
let nextVersion = isStableRelease ? packageJson.version : getNextBetaVersion(packageJson);
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ const env = { ...process.env };
|
||||
env.NODE_PATH = path.resolve(__dirname, '../out');
|
||||
|
||||
let testFiles = [
|
||||
'./addons/**/out/*api.js',
|
||||
'./addons/**/out-test/*api.js',
|
||||
'./out-test/**/*api.js',
|
||||
];
|
||||
|
||||
|
||||
@@ -88,19 +88,19 @@ function evalButtonCode(code) {
|
||||
if (code & 64) {
|
||||
button |= 4
|
||||
}
|
||||
let actionS = 'press';
|
||||
let action = 'press';
|
||||
let buttonS = reverseButtons[button];
|
||||
if (button === 3) {
|
||||
buttonS = '<none>';
|
||||
actionS = 'release';
|
||||
action = 'release';
|
||||
}
|
||||
if (move) {
|
||||
actionS = 'move';
|
||||
action = 'move';
|
||||
} else if (4 <= button && button <= 7) {
|
||||
buttonS = 'wheel';
|
||||
actionS = button === 4 ? 'up' : button === 5 ? 'down' : button === 6 ? 'left' : 'right';
|
||||
action = button === 4 ? 'up' : button === 5 ? 'down' : button === 6 ? 'left' : 'right';
|
||||
}
|
||||
return {button: buttonS, action: actionS, modifier};
|
||||
return {button: buttonS, action, modifier};
|
||||
}
|
||||
|
||||
// protocols
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ const paddingElement = <HTMLInputElement>document.getElementById('padding');
|
||||
|
||||
function setPadding(): void {
|
||||
term.element.style.padding = parseInt(paddingElement.value, 10).toString() + 'px';
|
||||
term.fit();
|
||||
addons.fit.instance.fit();
|
||||
}
|
||||
|
||||
function getSearchOptions(e: KeyboardEvent): ISearchOptions {
|
||||
|
||||
+9
-9
@@ -43,15 +43,15 @@ function startServer() {
|
||||
const env = Object.assign({}, process.env);
|
||||
env['COLORTERM'] = 'truecolor';
|
||||
var cols = parseInt(req.query.cols),
|
||||
rows = parseInt(req.query.rows),
|
||||
term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], {
|
||||
name: 'xterm-256color',
|
||||
cols: cols || 80,
|
||||
rows: rows || 24,
|
||||
cwd: env.PWD,
|
||||
env: env,
|
||||
encoding: USE_BINARY ? null : 'utf8'
|
||||
});
|
||||
rows = parseInt(req.query.rows),
|
||||
term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], {
|
||||
name: 'xterm-256color',
|
||||
cols: cols || 80,
|
||||
rows: rows || 24,
|
||||
cwd: process.platform === 'win32' ? undefined : env.PWD,
|
||||
env: env,
|
||||
encoding: USE_BINARY ? null : 'utf8'
|
||||
});
|
||||
|
||||
console.log('Created terminal with PID: ' + term.pid);
|
||||
terminals[term.pid] = term;
|
||||
|
||||
+13
-16
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "xterm",
|
||||
"description": "Full xterm terminal, in your browser",
|
||||
"version": "4.9.0",
|
||||
"version": "4.10.0",
|
||||
"main": "lib/xterm.js",
|
||||
"style": "css/xterm.css",
|
||||
"types": "typings/xterm.d.ts",
|
||||
@@ -18,9 +18,6 @@
|
||||
"test-api-chromium": "node ./bin/test_api.js --browser=chromium --timeout=20000",
|
||||
"test-api-firefox": "node ./bin/test_api.js --browser=firefox --timeout=20000",
|
||||
"test-api-webkit": "node ./bin/test_api.js --browser=webkit --timeout=20000",
|
||||
"pretest-api-chromium": "node ./bin/download_browser.js --browser chromium",
|
||||
"pretest-api-firefox": "node ./bin/download_browser.js --browser firefox",
|
||||
"pretest-api-webkit": "node ./bin/download_browser.js --browser webkit",
|
||||
"test-unit": "node ./bin/test.js",
|
||||
"test-unit-coverage": "node ./bin/test.js --coverage",
|
||||
"build": "tsc -b ./tsconfig.all.json",
|
||||
@@ -36,36 +33,36 @@
|
||||
"vtfeatures": "node bin/extract_vtfeatures.js src/**/*.ts src/*.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.12",
|
||||
"@types/chai": "^4.2.14",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/deep-equal": "^1.0.1",
|
||||
"@types/glob": "^7.1.3",
|
||||
"@types/jsdom": "^16.2.4",
|
||||
"@types/jsdom": "^16.2.5",
|
||||
"@types/mocha": "^8.0.3",
|
||||
"@types/node": "^10.17.17",
|
||||
"@types/utf8": "^2.1.6",
|
||||
"@types/webpack": "^4.41.22",
|
||||
"@types/ws": "^7.2.6",
|
||||
"@types/webpack": "^4.41.24",
|
||||
"@types/ws": "^7.2.9",
|
||||
"@typescript-eslint/eslint-plugin": "^4.0.0",
|
||||
"@typescript-eslint/parser": "^3.10.1",
|
||||
"chai": "^4.2.0",
|
||||
"deep-equal": "^2.0.3",
|
||||
"eslint": "^7.8.1",
|
||||
"deep-equal": "^2.0.4",
|
||||
"eslint": "^7.12.1",
|
||||
"express": "^4.17.1",
|
||||
"express-ws": "^4.0.0",
|
||||
"glob": "^7.0.5",
|
||||
"jsdom": "^16.4.0",
|
||||
"mocha": "^8.1.3",
|
||||
"mocha": "^8.2.1",
|
||||
"mustache": "^4.0.1",
|
||||
"node-pty": "^0.9.0",
|
||||
"nyc": "^15.1.0",
|
||||
"playwright-core": "^0.11.1",
|
||||
"source-map-loader": "^1.1.0",
|
||||
"ts-loader": "^8.0.3",
|
||||
"playwright": "^1.5.2",
|
||||
"source-map-loader": "^1.1.2",
|
||||
"ts-loader": "^8.0.8",
|
||||
"typescript": "4.0",
|
||||
"utf8": "^3.0.0",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack": "^5.4.0",
|
||||
"webpack-cli": "^4.2.0",
|
||||
"ws": "^7.3.1",
|
||||
"xterm-benchmark": "^0.1.3"
|
||||
}
|
||||
|
||||
@@ -56,8 +56,8 @@ export class AccessibilityManager extends Disposable {
|
||||
this._accessibilityTreeRoot.classList.add('xterm-accessibility');
|
||||
|
||||
this._rowContainer = document.createElement('div');
|
||||
this._rowContainer.setAttribute('role', 'list');
|
||||
this._rowContainer.classList.add('xterm-accessibility-tree');
|
||||
this._rowContainer.setAttribute('role', 'document');
|
||||
this._rowElements = [];
|
||||
for (let i = 0; i < this._terminal.rows; i++) {
|
||||
this._rowElements[i] = this._createAccessibilityTreeNode();
|
||||
@@ -263,7 +263,7 @@ export class AccessibilityManager extends Disposable {
|
||||
const element = this._rowElements[i];
|
||||
if (element) {
|
||||
if (lineData.length === 0) {
|
||||
element.innerHTML = ' ';
|
||||
element.innerText = '\u00a0';
|
||||
} else {
|
||||
element.textContent = lineData;
|
||||
}
|
||||
|
||||
@@ -89,8 +89,8 @@ export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextA
|
||||
export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, screenElement: HTMLElement, selectionService: ISelectionService, shouldSelectWord: boolean): void {
|
||||
moveTextAreaUnderMouseCursor(ev, textarea, screenElement);
|
||||
|
||||
if (shouldSelectWord && !selectionService.isClickInSelection(ev)) {
|
||||
selectionService.selectWordAtCursor(ev);
|
||||
if (shouldSelectWord) {
|
||||
selectionService.rightClickSelect(ev);
|
||||
}
|
||||
|
||||
// Get textarea ready to copy from the context menu
|
||||
|
||||
@@ -17,9 +17,8 @@ const DEFAULT_SELECTION = {
|
||||
rgba: 0xFFFFFF4D
|
||||
};
|
||||
|
||||
// An IIFE to generate DEFAULT_ANSI_COLORS. Do not mutate DEFAULT_ANSI_COLORS, instead make a copy
|
||||
// and mutate that.
|
||||
export const DEFAULT_ANSI_COLORS = (() => {
|
||||
// An IIFE to generate DEFAULT_ANSI_COLORS.
|
||||
export const DEFAULT_ANSI_COLORS = Object.freeze((() => {
|
||||
const colors = [
|
||||
// dark:
|
||||
css.toColor('#2e3436'),
|
||||
@@ -64,7 +63,7 @@ export const DEFAULT_ANSI_COLORS = (() => {
|
||||
}
|
||||
|
||||
return colors;
|
||||
})();
|
||||
})());
|
||||
|
||||
/**
|
||||
* Manages the source of truth for a terminal's colors.
|
||||
|
||||
+204
-139
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
import { MockViewport, MockCompositionHelper, MockRenderer, TestTerminal } from 'browser/TestUtils.test';
|
||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
@@ -11,6 +11,7 @@ import { IBufferService, IUnicodeService } from 'common/services/Services';
|
||||
import { Linkifier } from 'browser/Linkifier';
|
||||
import { MockLogService, MockUnicodeService } from 'common/TestUtils.test';
|
||||
import { IRegisteredLinkMatcher, IMouseZoneManager, IMouseZone } from 'browser/Types';
|
||||
import { IMarker } from 'common/Types';
|
||||
|
||||
const INIT_COLS = 80;
|
||||
const INIT_ROWS = 24;
|
||||
@@ -74,7 +75,7 @@ describe('Terminal', () => {
|
||||
it('should fire a key event after a keypress DOM event', (done) => {
|
||||
term.onKey(e => {
|
||||
assert.equal(typeof e.key, 'string');
|
||||
expect(e.domEvent).to.be.an.instanceof(Object);
|
||||
assert.equal(e.domEvent instanceof Object, true);
|
||||
done();
|
||||
});
|
||||
const evKeyPress = <KeyboardEvent>{
|
||||
@@ -88,7 +89,7 @@ describe('Terminal', () => {
|
||||
it('should fire a key event after a keydown DOM event', (done) => {
|
||||
term.onKey(e => {
|
||||
assert.equal(typeof e.key, 'string');
|
||||
expect(e.domEvent).to.be.an.instanceof(Object);
|
||||
assert.equal(e.domEvent instanceof Object, true);
|
||||
done();
|
||||
});
|
||||
(<any>term).textarea = { value: '' };
|
||||
@@ -102,7 +103,6 @@ describe('Terminal', () => {
|
||||
});
|
||||
it('should fire the onResize event', (done) => {
|
||||
term.onResize(e => {
|
||||
expect(e).to.have.keys(['cols', 'rows']);
|
||||
assert.equal(typeof e.cols, 'number');
|
||||
assert.equal(typeof e.rows, 'number');
|
||||
done();
|
||||
@@ -723,10 +723,10 @@ describe('Terminal', () => {
|
||||
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
||||
term.writeSync(high + String.fromCharCode(i));
|
||||
const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
|
||||
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
|
||||
expect(tchar.getChars().length).eql(2);
|
||||
expect(tchar.getWidth()).eql(1);
|
||||
expect(term.buffer.lines.get(0)!.loadCell(1, cell).getChars()).eql('');
|
||||
assert.equal(tchar.getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(tchar.getChars().length, 2);
|
||||
assert.equal(tchar.getWidth(), 1);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
@@ -736,9 +736,9 @@ describe('Terminal', () => {
|
||||
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
||||
term.buffer.x = term.cols - 1;
|
||||
term.writeSync(high + String.fromCharCode(i));
|
||||
expect(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars()).eql(high + String.fromCharCode(i));
|
||||
expect(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars().length).eql(2);
|
||||
expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars()).eql('');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars().length, 2);
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
@@ -749,10 +749,10 @@ describe('Terminal', () => {
|
||||
term.buffer.x = term.cols - 1;
|
||||
|
||||
term.writeSync('a' + high + String.fromCharCode(i));
|
||||
expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars()).eql('a');
|
||||
expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars()).eql(high + String.fromCharCode(i));
|
||||
expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars().length).eql(2);
|
||||
expect(term.buffer.lines.get(1)!.loadCell(1, cell).getChars()).eql('');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), 'a');
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars().length, 2);
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
@@ -768,9 +768,9 @@ describe('Terminal', () => {
|
||||
}
|
||||
term.writeSync('a' + high + String.fromCharCode(i));
|
||||
// auto wraparound mode should cut off the rest of the line
|
||||
expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars()).eql(high + String.fromCharCode(i));
|
||||
expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars().length).eql(2);
|
||||
expect(term.buffer.lines.get(1)!.loadCell(1, cell).getChars()).eql('');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars().length, 2);
|
||||
assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
@@ -781,10 +781,10 @@ describe('Terminal', () => {
|
||||
term.writeSync(high);
|
||||
term.writeSync(String.fromCharCode(i));
|
||||
const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
|
||||
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
|
||||
expect(tchar.getChars().length).eql(2);
|
||||
expect(tchar.getWidth()).eql(1);
|
||||
expect(term.buffer.lines.get(0)!.loadCell(1, cell).getChars()).eql('');
|
||||
assert.equal(tchar.getChars(), high + String.fromCharCode(i));
|
||||
assert.equal(tchar.getChars().length, 2);
|
||||
assert.equal(tchar.getWidth(), 1);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), '');
|
||||
term.reset();
|
||||
}
|
||||
});
|
||||
@@ -795,81 +795,81 @@ describe('Terminal', () => {
|
||||
it('café', () => {
|
||||
term.writeSync('cafe\u0301');
|
||||
term.buffer.lines.get(0)!.loadCell(3, cell);
|
||||
expect(cell.getChars()).eql('e\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), 'e\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
});
|
||||
it('café - end of line', () => {
|
||||
term.buffer.x = term.cols - 1 - 3;
|
||||
term.writeSync('cafe\u0301');
|
||||
term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
|
||||
expect(cell.getChars()).eql('e\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), 'e\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
term.buffer.lines.get(0)!.loadCell(1, cell);
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
});
|
||||
it('multiple combined é', () => {
|
||||
term.writeSync(Array(100).join('e\u0301'));
|
||||
for (let i = 0; i < term.cols; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
expect(cell.getChars()).eql('e\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), 'e\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
}
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('e\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), 'e\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
});
|
||||
it('multiple surrogate with combined', () => {
|
||||
term.writeSync(Array(100).join('\uD800\uDC00\u0301'));
|
||||
for (let i = 0; i < term.cols; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
expect(cell.getChars()).eql('\uD800\uDC00\u0301');
|
||||
expect(cell.getChars().length).eql(3);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), '\uD800\uDC00\u0301');
|
||||
assert.equal(cell.getChars().length, 3);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
}
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('\uD800\uDC00\u0301');
|
||||
expect(cell.getChars().length).eql(3);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), '\uD800\uDC00\u0301');
|
||||
assert.equal(cell.getChars().length, 3);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unicode - fullwidth characters', () => {
|
||||
const cell = new CellData();
|
||||
it('cursor movement even', () => {
|
||||
expect(term.buffer.x).eql(0);
|
||||
assert.equal(term.buffer.x, 0);
|
||||
term.writeSync('¥');
|
||||
expect(term.buffer.x).eql(2);
|
||||
assert.equal(term.buffer.x, 2);
|
||||
});
|
||||
it('cursor movement odd', () => {
|
||||
term.buffer.x = 1;
|
||||
expect(term.buffer.x).eql(1);
|
||||
assert.equal(term.buffer.x, 1);
|
||||
term.writeSync('¥');
|
||||
expect(term.buffer.x).eql(3);
|
||||
assert.equal(term.buffer.x, 3);
|
||||
});
|
||||
it('line of ¥ even', () => {
|
||||
term.writeSync(Array(50).join('¥'));
|
||||
for (let i = 0; i < term.cols; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
if (i % 2) {
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(0);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 0);
|
||||
} else {
|
||||
expect(cell.getChars()).eql('¥');
|
||||
expect(cell.getChars().length).eql(1);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥');
|
||||
assert.equal(cell.getChars().length, 1);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
}
|
||||
}
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('¥');
|
||||
expect(cell.getChars().length).eql(1);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥');
|
||||
assert.equal(cell.getChars().length, 1);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
});
|
||||
it('line of ¥ odd', () => {
|
||||
term.buffer.x = 1;
|
||||
@@ -877,23 +877,23 @@ describe('Terminal', () => {
|
||||
for (let i = 1; i < term.cols - 1; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
if (!(i % 2)) {
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(0);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 0);
|
||||
} else {
|
||||
expect(cell.getChars()).eql('¥');
|
||||
expect(cell.getChars().length).eql(1);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥');
|
||||
assert.equal(cell.getChars().length, 1);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
}
|
||||
}
|
||||
term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('¥');
|
||||
expect(cell.getChars().length).eql(1);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥');
|
||||
assert.equal(cell.getChars().length, 1);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
});
|
||||
it('line of ¥ with combining odd', () => {
|
||||
term.buffer.x = 1;
|
||||
@@ -901,42 +901,42 @@ describe('Terminal', () => {
|
||||
for (let i = 1; i < term.cols - 1; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
if (!(i % 2)) {
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(0);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 0);
|
||||
} else {
|
||||
expect(cell.getChars()).eql('¥\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
}
|
||||
}
|
||||
term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('¥\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
});
|
||||
it('line of ¥ with combining even', () => {
|
||||
term.writeSync(Array(50).join('¥\u0301'));
|
||||
for (let i = 0; i < term.cols; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
if (i % 2) {
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(0);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 0);
|
||||
} else {
|
||||
expect(cell.getChars()).eql('¥\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
}
|
||||
}
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('¥\u0301');
|
||||
expect(cell.getChars().length).eql(2);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '¥\u0301');
|
||||
assert.equal(cell.getChars().length, 2);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
});
|
||||
it('line of surrogate fullwidth with combining odd', () => {
|
||||
term.buffer.x = 1;
|
||||
@@ -944,42 +944,42 @@ describe('Terminal', () => {
|
||||
for (let i = 1; i < term.cols - 1; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
if (!(i % 2)) {
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(0);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 0);
|
||||
} else {
|
||||
expect(cell.getChars()).eql('\ud843\ude6d\u0301');
|
||||
expect(cell.getChars().length).eql(3);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '\ud843\ude6d\u0301');
|
||||
assert.equal(cell.getChars().length, 3);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
}
|
||||
}
|
||||
term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(1);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 1);
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('\ud843\ude6d\u0301');
|
||||
expect(cell.getChars().length).eql(3);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '\ud843\ude6d\u0301');
|
||||
assert.equal(cell.getChars().length, 3);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
});
|
||||
it('line of surrogate fullwidth with combining even', () => {
|
||||
term.writeSync(Array(50).join('\ud843\ude6d\u0301'));
|
||||
for (let i = 0; i < term.cols; ++i) {
|
||||
term.buffer.lines.get(0)!.loadCell(i, cell);
|
||||
if (i % 2) {
|
||||
expect(cell.getChars()).eql('');
|
||||
expect(cell.getChars().length).eql(0);
|
||||
expect(cell.getWidth()).eql(0);
|
||||
assert.equal(cell.getChars(), '');
|
||||
assert.equal(cell.getChars().length, 0);
|
||||
assert.equal(cell.getWidth(), 0);
|
||||
} else {
|
||||
expect(cell.getChars()).eql('\ud843\ude6d\u0301');
|
||||
expect(cell.getChars().length).eql(3);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '\ud843\ude6d\u0301');
|
||||
assert.equal(cell.getChars().length, 3);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
}
|
||||
}
|
||||
term.buffer.lines.get(1)!.loadCell(0, cell);
|
||||
expect(cell.getChars()).eql('\ud843\ude6d\u0301');
|
||||
expect(cell.getChars().length).eql(3);
|
||||
expect(cell.getWidth()).eql(2);
|
||||
assert.equal(cell.getChars(), '\ud843\ude6d\u0301');
|
||||
assert.equal(cell.getChars().length, 3);
|
||||
assert.equal(cell.getWidth(), 2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -991,11 +991,11 @@ describe('Terminal', () => {
|
||||
term.buffer.y = 0;
|
||||
term.write('\x1b[4h');
|
||||
term.writeSync('abcde');
|
||||
expect(term.buffer.lines.get(0)!.length).eql(term.cols);
|
||||
expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('a');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(14, cell).getChars()).eql('e');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(15, cell).getChars()).eql('0');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql('4');
|
||||
assert.equal(term.buffer.lines.get(0)!.length, term.cols);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(10, cell).getChars(), 'a');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(14, cell).getChars(), 'e');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(15, cell).getChars(), '0');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(79, cell).getChars(), '4');
|
||||
});
|
||||
it('fullwidth - insert', () => {
|
||||
term.writeSync(Array(9).join('0123456789').slice(-80));
|
||||
@@ -1003,12 +1003,12 @@ describe('Terminal', () => {
|
||||
term.buffer.y = 0;
|
||||
term.write('\x1b[4h');
|
||||
term.writeSync('¥¥¥');
|
||||
expect(term.buffer.lines.get(0)!.length).eql(term.cols);
|
||||
expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('¥');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(14, cell).getChars()).eql('¥');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(15, cell).getChars()).eql('');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql('3');
|
||||
assert.equal(term.buffer.lines.get(0)!.length, term.cols);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(10, cell).getChars(), '¥');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(11, cell).getChars(), '');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(14, cell).getChars(), '¥');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(15, cell).getChars(), '');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(79, cell).getChars(), '3');
|
||||
});
|
||||
it('fullwidth - right border', () => {
|
||||
term.writeSync(Array(41).join('¥'));
|
||||
@@ -1016,15 +1016,15 @@ describe('Terminal', () => {
|
||||
term.buffer.y = 0;
|
||||
term.write('\x1b[4h');
|
||||
term.writeSync('a');
|
||||
expect(term.buffer.lines.get(0)!.length).eql(term.cols);
|
||||
expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('a');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('¥');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql(''); // fullwidth char got replaced
|
||||
assert.equal(term.buffer.lines.get(0)!.length, term.cols);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(10, cell).getChars(), 'a');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(11, cell).getChars(), '¥');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(79, cell).getChars(), ''); // fullwidth char got replaced
|
||||
term.writeSync('b');
|
||||
expect(term.buffer.lines.get(0)!.length).eql(term.cols);
|
||||
expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('b');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(12, cell).getChars()).eql('¥');
|
||||
expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql(''); // empty cell after fullwidth
|
||||
assert.equal(term.buffer.lines.get(0)!.length, term.cols);
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(11, cell).getChars(), 'b');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(12, cell).getChars(), '¥');
|
||||
assert.equal(term.buffer.lines.get(0)!.loadCell(79, cell).getChars(), ''); // empty cell after fullwidth
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1345,7 +1345,7 @@ describe('Terminal', () => {
|
||||
];
|
||||
terminal.writeSync(data.join(''));
|
||||
// brute force test with insane values
|
||||
expect(() => {
|
||||
assert.doesNotThrow(() => {
|
||||
for (let overscan = 0; overscan < 20; ++overscan) {
|
||||
for (let start = -10; start < 20; ++start) {
|
||||
for (let end = -10; end < 20; ++end) {
|
||||
@@ -1356,7 +1356,7 @@ describe('Terminal', () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}).to.not.throw();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1405,18 +1405,18 @@ describe('Terminal', () => {
|
||||
// not converting
|
||||
const termNotConverting = new TestTerminal({cols: 15, rows: 10});
|
||||
termNotConverting.writeSync('Hello\nWorld');
|
||||
expect(termNotConverting.buffer.lines.get(0)!.translateToString(false)).equals('Hello ');
|
||||
expect(termNotConverting.buffer.lines.get(1)!.translateToString(false)).equals(' World ');
|
||||
expect(termNotConverting.buffer.lines.get(0)!.translateToString(true)).equals('Hello');
|
||||
expect(termNotConverting.buffer.lines.get(1)!.translateToString(true)).equals(' World');
|
||||
assert.equal(termNotConverting.buffer.lines.get(0)!.translateToString(false), 'Hello ');
|
||||
assert.equal(termNotConverting.buffer.lines.get(1)!.translateToString(false), ' World ');
|
||||
assert.equal(termNotConverting.buffer.lines.get(0)!.translateToString(true), 'Hello');
|
||||
assert.equal(termNotConverting.buffer.lines.get(1)!.translateToString(true), ' World');
|
||||
|
||||
// converting
|
||||
const termConverting = new TestTerminal({cols: 15, rows: 10, convertEol: true});
|
||||
termConverting.writeSync('Hello\nWorld');
|
||||
expect(termConverting.buffer.lines.get(0)!.translateToString(false)).equals('Hello ');
|
||||
expect(termConverting.buffer.lines.get(1)!.translateToString(false)).equals('World ');
|
||||
expect(termConverting.buffer.lines.get(0)!.translateToString(true)).equals('Hello');
|
||||
expect(termConverting.buffer.lines.get(1)!.translateToString(true)).equals('World');
|
||||
assert.equal(termConverting.buffer.lines.get(0)!.translateToString(false), 'Hello ');
|
||||
assert.equal(termConverting.buffer.lines.get(1)!.translateToString(false), 'World ');
|
||||
assert.equal(termConverting.buffer.lines.get(0)!.translateToString(true), 'Hello');
|
||||
assert.equal(termConverting.buffer.lines.get(1)!.translateToString(true), 'World');
|
||||
});
|
||||
describe('Terminal InputHandler integration', () => {
|
||||
function getLines(term: TestTerminal, limit: number = term.rows): string[] {
|
||||
@@ -1533,6 +1533,71 @@ describe('Terminal', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// FIXME: move to common/CoreTerminal.test once the trimming is moved over
|
||||
describe('marker lifecycle', () => {
|
||||
// create a 10x5 terminal with markers on every line
|
||||
// to test marker lifecycle under various terminal actions
|
||||
let markers: IMarker[];
|
||||
let disposeStack: IMarker[];
|
||||
let term: TestTerminal;
|
||||
beforeEach(() => {
|
||||
term = new TestTerminal({});
|
||||
markers = [];
|
||||
disposeStack = [];
|
||||
term.optionsService.setOption('scrollback', 1);
|
||||
term.resize(10, 5);
|
||||
markers.push(term.buffers.active.addMarker(term.buffers.active.y));
|
||||
term.writeSync('\x1b[r0\r\n');
|
||||
markers.push(term.buffers.active.addMarker(term.buffers.active.y));
|
||||
term.writeSync('1\r\n');
|
||||
markers.push(term.buffers.active.addMarker(term.buffers.active.y));
|
||||
term.writeSync('2\r\n');
|
||||
markers.push(term.buffers.active.addMarker(term.buffers.active.y));
|
||||
term.writeSync('3\r\n');
|
||||
markers.push(term.buffers.active.addMarker(term.buffers.active.y));
|
||||
term.writeSync('4');
|
||||
for (let i = 0; i < markers.length; ++i) {
|
||||
const marker = markers[i];
|
||||
marker.onDispose(() => disposeStack.push(marker));
|
||||
}
|
||||
});
|
||||
it('initial', () => {
|
||||
assert.deepEqual(markers.map(m => m.line), [0, 1, 2, 3, 4]);
|
||||
});
|
||||
it('should dispose on normal trim off the top', () => {
|
||||
// moves top line into scrollback
|
||||
term.writeSync('\n');
|
||||
assert.deepEqual(disposeStack, []);
|
||||
// trims first marker
|
||||
term.writeSync('\n');
|
||||
assert.deepEqual(disposeStack, [markers[0]]);
|
||||
// trims second marker
|
||||
term.writeSync('\n');
|
||||
assert.deepEqual(disposeStack, [markers[0], markers[1]]);
|
||||
// trimmed marker objs should be disposed
|
||||
assert.deepEqual(disposeStack.map(el => el.isDisposed), [true, true]);
|
||||
assert.deepEqual(disposeStack.map(el => (el as any)._isDisposed), [true, true]);
|
||||
// trimmed markers should contain line -1
|
||||
assert.deepEqual(disposeStack.map(el => el.line), [-1, -1]);
|
||||
});
|
||||
it('should dispose on DL', () => {
|
||||
term.writeSync('\x1b[3;1H'); // move cursor to 0, 2
|
||||
term.writeSync('\x1b[2M'); // delete 2 lines
|
||||
assert.deepEqual(disposeStack, [markers[2], markers[3]]);
|
||||
});
|
||||
it('should dispose on IL', () => {
|
||||
term.writeSync('\x1b[3;1H'); // move cursor to 0, 2
|
||||
term.writeSync('\x1b[2L'); // insert 2 lines
|
||||
assert.deepEqual(disposeStack, [markers[4], markers[3]]);
|
||||
assert.deepEqual(markers.map(el => el.line), [0, 1, 4, -1, -1]);
|
||||
});
|
||||
it('should dispose on resize', () => {
|
||||
term.resize(10, 2);
|
||||
assert.deepEqual(disposeStack, [markers[0], markers[1]]);
|
||||
assert.deepEqual(markers.map(el => el.line), [-1, -1, 0, 1, 2]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class TestLinkifier extends Linkifier {
|
||||
|
||||
+17
-1
@@ -39,7 +39,7 @@ import { MouseZoneManager } from 'browser/MouseZoneManager';
|
||||
import { AccessibilityManager } from './AccessibilityManager';
|
||||
import { ITheme, IMarker, IDisposable, ISelectionPosition, ILinkProvider } from 'xterm';
|
||||
import { DomRenderer } from 'browser/renderer/dom/DomRenderer';
|
||||
import { IKeyboardEvent, KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions } from 'common/Types';
|
||||
import { IKeyboardEvent, KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions, IAnsiColorChangeEvent } from 'common/Types';
|
||||
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
|
||||
import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
|
||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
@@ -53,6 +53,7 @@ import { Linkifier2 } from 'browser/Linkifier2';
|
||||
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
|
||||
import { CoreTerminal } from 'common/CoreTerminal';
|
||||
import { ITerminalOptions as IInitializedTerminalOptions } from 'common/services/Services';
|
||||
import { rgba } from 'browser/Color';
|
||||
|
||||
// Let it work inside Node.js for automated testing purposes.
|
||||
const document: Document = (typeof window !== 'undefined') ? window.document : null as any;
|
||||
@@ -148,6 +149,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this.register(this._inputHandler.onRequestReset(() => this.reset()));
|
||||
this.register(this._inputHandler.onRequestScroll((eraseAttr, isWrapped) => this.scroll(eraseAttr, isWrapped || undefined)));
|
||||
this.register(this._inputHandler.onRequestWindowsOptionsReport(type => this._reportWindowsOptions(type)));
|
||||
this.register(this._inputHandler.onAnsiColorChange((event) => this._changeAnsiColor(event)));
|
||||
this.register(forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove));
|
||||
this.register(forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange));
|
||||
this.register(forwardEvent(this._inputHandler.onA11yChar, this._onA11yCharEmitter));
|
||||
@@ -157,6 +159,19 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this.register(this._bufferService.onResize(e => this._afterResize(e.cols, e.rows)));
|
||||
}
|
||||
|
||||
private _changeAnsiColor(event: IAnsiColorChangeEvent): void {
|
||||
if (!this._colorManager) { return; }
|
||||
|
||||
event.colors.forEach(ansiColor => {
|
||||
const color = rgba.toColor(ansiColor.red, ansiColor.green, ansiColor.blue);
|
||||
|
||||
this._colorManager!.colors.ansi[ansiColor.colorIndex] = color;
|
||||
});
|
||||
|
||||
this._renderService?.setColors(this._colorManager!.colors);
|
||||
this.viewport?.onThemeChange(this._colorManager!.colors);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this._isDisposed) {
|
||||
return;
|
||||
@@ -383,6 +398,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
this.element.classList.add('terminal');
|
||||
this.element.classList.add('xterm');
|
||||
this.element.setAttribute('tabindex', '0');
|
||||
this.element.setAttribute('role', 'document');
|
||||
parent.appendChild(this.element);
|
||||
|
||||
// Performance: Use a document fragment to build the terminal
|
||||
|
||||
@@ -32,7 +32,7 @@ if (os.platform() === 'darwin') {
|
||||
);
|
||||
}
|
||||
// filter skipFilenames
|
||||
const FILES = TESTFILES.filter(value => SKIP_FILES.indexOf(value.split('/').slice(-1)[0]) === -1);
|
||||
const FILES = TESTFILES.filter(value => !SKIP_FILES.includes(value.split('/').slice(-1)[0]));
|
||||
|
||||
describe('Escape Sequence Files', function(): void {
|
||||
this.timeout(1000);
|
||||
@@ -104,7 +104,7 @@ describe('Escape Sequence Files', function(): void {
|
||||
function formatError(input: string, output: string, expected: string): string {
|
||||
function addLineNumber(start: number, color: string): (s: string) => string {
|
||||
let counter = start || 0;
|
||||
return function(s: string): string {
|
||||
return (s: string): string => {
|
||||
counter += 1;
|
||||
return '\x1b[33m' + (' ' + counter).slice(-2) + color + s;
|
||||
};
|
||||
|
||||
@@ -35,6 +35,11 @@ export class CompositionHelper {
|
||||
*/
|
||||
private _isSendingComposition: boolean;
|
||||
|
||||
/**
|
||||
* Data already sent due to keydown event.
|
||||
*/
|
||||
private _dataAlreadySent: string;
|
||||
|
||||
constructor(
|
||||
private readonly _textarea: HTMLTextAreaElement,
|
||||
private readonly _compositionView: HTMLElement,
|
||||
@@ -46,6 +51,7 @@ export class CompositionHelper {
|
||||
this._isComposing = false;
|
||||
this._isSendingComposition = false;
|
||||
this._compositionPosition = { start: 0, end: 0 };
|
||||
this._dataAlreadySent = '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,6 +61,7 @@ export class CompositionHelper {
|
||||
this._isComposing = true;
|
||||
this._compositionPosition.start = this._textarea.value.length;
|
||||
this._compositionView.textContent = '';
|
||||
this._dataAlreadySent = '';
|
||||
this._compositionView.classList.add('active');
|
||||
}
|
||||
|
||||
@@ -147,6 +154,9 @@ export class CompositionHelper {
|
||||
if (this._isSendingComposition) {
|
||||
this._isSendingComposition = false;
|
||||
let input;
|
||||
// Add length of data already sent due to keydown event,
|
||||
// otherwise input characters can be duplicated. (Issue #3191)
|
||||
currentCompositionPosition.start += this._dataAlreadySent.length;
|
||||
if (this._isComposing) {
|
||||
// Use the end position to get the string if a new composition has started.
|
||||
input = this._textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end);
|
||||
@@ -156,7 +166,9 @@ export class CompositionHelper {
|
||||
// (eg. 2) after a composition character.
|
||||
input = this._textarea.value.substring(currentCompositionPosition.start);
|
||||
}
|
||||
this._coreService.triggerDataEvent(input, true);
|
||||
if (input.length > 0) {
|
||||
this._coreService.triggerDataEvent(input, true);
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
@@ -176,6 +188,7 @@ export class CompositionHelper {
|
||||
const newValue = this._textarea.value;
|
||||
const diff = newValue.replace(oldValue, '');
|
||||
if (diff.length > 0) {
|
||||
this._dataAlreadySent = diff;
|
||||
this._coreService.triggerDataEvent(diff, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,13 @@ import * as Strings from '../LocalizableStrings';
|
||||
import { IEvent, EventEmitter } from 'common/EventEmitter';
|
||||
import { AddonManager } from './AddonManager';
|
||||
import { IParams } from 'common/parser/Types';
|
||||
import { BufferSet } from 'common/buffer/BufferSet';
|
||||
|
||||
export class Terminal implements ITerminalApi {
|
||||
private _core: ITerminal;
|
||||
private _addonManager: AddonManager;
|
||||
private _parser: IParser | undefined;
|
||||
private _buffer: BufferNamespaceApi | undefined;
|
||||
|
||||
constructor(options?: ITerminalOptions) {
|
||||
this._core = new TerminalCore(options);
|
||||
@@ -58,7 +60,10 @@ export class Terminal implements ITerminalApi {
|
||||
public get cols(): number { return this._core.cols; }
|
||||
public get buffer(): IBufferNamespaceApi {
|
||||
this._checkProposedApi();
|
||||
return new BufferNamespaceApi(this._core.buffers);
|
||||
if (!this._buffer) {
|
||||
this._buffer = new BufferNamespaceApi(this._core);
|
||||
}
|
||||
return this._buffer;
|
||||
}
|
||||
public get markers(): ReadonlyArray<IMarker> {
|
||||
this._checkProposedApi();
|
||||
@@ -170,7 +175,7 @@ export class Terminal implements ITerminalApi {
|
||||
this._core.paste(data);
|
||||
}
|
||||
public getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'logLevel' | 'rendererType' | 'termName' | 'wordSeparator'): string;
|
||||
public getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'visualBell'): boolean;
|
||||
public getOption(key: 'allowTransparency' | 'altClickMovesCursor' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'visualBell'): boolean;
|
||||
public getOption(key: 'cols' | 'fontSize' | 'letterSpacing' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback'): number;
|
||||
public getOption(key: 'fontWeight' | 'fontWeightBold'): FontWeight;
|
||||
public getOption(key: string): any;
|
||||
@@ -182,7 +187,7 @@ export class Terminal implements ITerminalApi {
|
||||
public setOption(key: 'logLevel', value: 'debug' | 'info' | 'warn' | 'error' | 'off'): void;
|
||||
public setOption(key: 'bellStyle', value: 'none' | 'visual' | 'sound' | 'both'): void;
|
||||
public setOption(key: 'cursorStyle', value: 'block' | 'underline' | 'bar'): void;
|
||||
public setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'visualBell', value: boolean): void;
|
||||
public setOption(key: 'allowTransparency' | 'altClickMovesCursor' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'visualBell', value: boolean): void;
|
||||
public setOption(key: 'fontSize' | 'letterSpacing' | 'lineHeight' | 'tabStopWidth' | 'scrollback', value: number): void;
|
||||
public setOption(key: 'theme', value: ITheme): void;
|
||||
public setOption(key: 'cols' | 'rows', value: number): void;
|
||||
@@ -245,21 +250,21 @@ class BufferNamespaceApi implements IBufferNamespaceApi {
|
||||
private _onBufferChange = new EventEmitter<IBufferApi>();
|
||||
public get onBufferChange(): IEvent<IBufferApi> { return this._onBufferChange.event; }
|
||||
|
||||
constructor(private _buffers: IBufferSet) {
|
||||
this._normal = new BufferApiView(this._buffers.normal, 'normal');
|
||||
this._alternate = new BufferApiView(this._buffers.alt, 'alternate');
|
||||
this._buffers.onBufferActivate(() => this._onBufferChange.fire(this.active));
|
||||
constructor(private _core: ITerminal) {
|
||||
this._normal = new BufferApiView(this._core.buffers.normal, 'normal');
|
||||
this._alternate = new BufferApiView(this._core.buffers.alt, 'alternate');
|
||||
this._core.buffers.onBufferActivate(() => this._onBufferChange.fire(this.active));
|
||||
}
|
||||
public get active(): IBufferApi {
|
||||
if (this._buffers.active === this._buffers.normal) { return this.normal; }
|
||||
if (this._buffers.active === this._buffers.alt) { return this.alternate; }
|
||||
if (this._core.buffers.active === this._core.buffers.normal) { return this.normal; }
|
||||
if (this._core.buffers.active === this._core.buffers.alt) { return this.alternate; }
|
||||
throw new Error('Active buffer is neither normal nor alternate');
|
||||
}
|
||||
public get normal(): IBufferApi {
|
||||
return this._normal.init(this._buffers.normal);
|
||||
return this._normal.init(this._core.buffers.normal);
|
||||
}
|
||||
public get alternate(): IBufferApi {
|
||||
return this._alternate.init(this._buffers.alt);
|
||||
return this._alternate.init(this._core.buffers.alt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,7 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number
|
||||
cursor: undefined,
|
||||
cursorAccent: undefined,
|
||||
selection: undefined,
|
||||
// For the static char atlas, we only use the first 16 colors, but we need all 256 for the
|
||||
// dynamic character atlas.
|
||||
ansi: colors.ansi.slice(0, 16)
|
||||
ansi: colors.ansi
|
||||
};
|
||||
return {
|
||||
devicePixelRatio: window.devicePixelRatio,
|
||||
|
||||
@@ -138,7 +138,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
` width: ${this.dimensions.actualCellWidth}px` +
|
||||
`}`;
|
||||
|
||||
this._dimensionsStyleElement.innerHTML = styles;
|
||||
this._dimensionsStyleElement.textContent = styles;
|
||||
|
||||
this._selectionContainer.style.height = this._viewportElement.style.height;
|
||||
this._screenElement.style.width = `${this.dimensions.canvasWidth}px`;
|
||||
@@ -237,7 +237,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(this._colors.background).css}; }` +
|
||||
`${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${this._colors.foreground.css}; }`;
|
||||
|
||||
this._themeStyleElement.innerHTML = styles;
|
||||
this._themeStyleElement.textContent = styles;
|
||||
}
|
||||
|
||||
public onDevicePixelRatioChange(): void {
|
||||
@@ -348,7 +348,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
|
||||
public clear(): void {
|
||||
for (const e of this._rowElements) {
|
||||
e.innerHTML = '';
|
||||
e.innerText = '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +359,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
|
||||
for (let y = start; y <= end; y++) {
|
||||
const rowElement = this._rowElements[y];
|
||||
rowElement.innerHTML = '';
|
||||
rowElement.innerText = '';
|
||||
|
||||
const row = y + this._bufferService.buffer.ydisp;
|
||||
const lineData = this._bufferService.buffer.lines.get(row);
|
||||
|
||||
@@ -14,6 +14,7 @@ export class CoreBrowserService implements ICoreBrowserService {
|
||||
}
|
||||
|
||||
public get isFocused(): boolean {
|
||||
return document.activeElement === this._textarea && document.hasFocus();
|
||||
const docOrShadowRoot = this._textarea.getRootNode ? this._textarea.getRootNode() as Document | ShadowRoot : document;
|
||||
return docOrShadowRoot.activeElement === this._textarea && document.hasFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
private _rowCount: number,
|
||||
screenElement: HTMLElement,
|
||||
@IOptionsService optionsService: IOptionsService,
|
||||
@ICharSizeService charSizeService: ICharSizeService,
|
||||
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
||||
@IBufferService bufferService: IBufferService
|
||||
) {
|
||||
super();
|
||||
@@ -67,7 +67,7 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
|
||||
this.register(bufferService.onResize(e => this._fullRefresh()));
|
||||
this.register(optionsService.onOptionChange(() => this._renderer.onOptionsChanged()));
|
||||
this.register(charSizeService.onCharSizeChange(() => this.onCharSizeChanged()));
|
||||
this.register(this._charSizeService.onCharSizeChange(() => this.onCharSizeChanged()));
|
||||
|
||||
// No need to register this as renderer is explicitly disposed in RenderService.dispose
|
||||
this._renderer.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));
|
||||
@@ -87,6 +87,12 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
|
||||
private _onIntersectionChange(entry: IntersectionObserverEntry): void {
|
||||
this._isPaused = entry.isIntersecting === undefined ? (entry.intersectionRatio === 0) : !entry.isIntersecting;
|
||||
|
||||
// Terminal was hidden on open
|
||||
if (!this._isPaused && !this._charSizeService.hasValidSize) {
|
||||
this._charSizeService.measure();
|
||||
}
|
||||
|
||||
if (!this._isPaused && this._needsFullRefresh) {
|
||||
this.refreshRows(0, this._rowCount - 1);
|
||||
this._needsFullRefresh = false;
|
||||
|
||||
@@ -105,6 +105,9 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
private _workCell: CellData = new CellData();
|
||||
|
||||
private _mouseDownTimeStamp: number = 0;
|
||||
private _oldHasSelection: boolean = false;
|
||||
private _oldSelectionStart: [number, number] | undefined = undefined;
|
||||
private _oldSelectionEnd: [number, number] | undefined = undefined;
|
||||
|
||||
private _onLinuxMouseSelection = this.register(new EventEmitter<string>());
|
||||
public get onLinuxMouseSelection(): IEvent<string> { return this._onLinuxMouseSelection.event; }
|
||||
@@ -290,7 +293,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
* Checks if the current click was inside the current selection
|
||||
* @param event The mouse event
|
||||
*/
|
||||
public isClickInSelection(event: MouseEvent): boolean {
|
||||
private _isClickInSelection(event: MouseEvent): boolean {
|
||||
const coords = this._getMouseBufferCoords(event);
|
||||
const start = this._model.finalSelectionStart;
|
||||
const end = this._model.finalSelectionEnd;
|
||||
@@ -313,7 +316,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
* Selects word at the current mouse event coordinates.
|
||||
* @param event The mouse event.
|
||||
*/
|
||||
public selectWordAtCursor(event: MouseEvent): void {
|
||||
private _selectWordAtCursor(event: MouseEvent): void {
|
||||
const coords = this._getMouseBufferCoords(event);
|
||||
if (coords) {
|
||||
this._selectWordAt(coords, false);
|
||||
@@ -667,7 +670,7 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
|
||||
this._removeMouseDownListeners();
|
||||
|
||||
if (this.selectionText.length <= 1 && timeElapsed < ALT_CLICK_MOVE_CURSOR_TIME && event.altKey) {
|
||||
if (this.selectionText.length <= 1 && timeElapsed < ALT_CLICK_MOVE_CURSOR_TIME && event.altKey && this._optionsService.getOption('altClickMovesCursor')) {
|
||||
if (this._bufferService.buffer.ybase === this._bufferService.buffer.ydisp) {
|
||||
const coordinates = this._mouseService.getCoords(
|
||||
event,
|
||||
@@ -681,11 +684,43 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
this._coreService.triggerDataEvent(sequence, true);
|
||||
}
|
||||
}
|
||||
} else if (this.hasSelection) {
|
||||
this._onSelectionChange.fire();
|
||||
} else {
|
||||
this._fireEventIfSelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private _fireEventIfSelectionChanged(): void {
|
||||
const start = this._model.finalSelectionStart;
|
||||
const end = this._model.finalSelectionEnd;
|
||||
const hasSelection = !!start && !!end && (start[0] !== end[0] || start[1] !== end[1]);
|
||||
|
||||
if (!hasSelection) {
|
||||
if (this._oldHasSelection) {
|
||||
this._fireOnSelectionChange(start, end, hasSelection);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check, these should not be undefined as there is a selection
|
||||
if (!start || !end) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._oldSelectionStart || !this._oldSelectionEnd || (
|
||||
start[0] !== this._oldSelectionStart[0] || start[1] !== this._oldSelectionStart[1] ||
|
||||
end[0] !== this._oldSelectionEnd[0] || end[1] !== this._oldSelectionEnd[1])) {
|
||||
|
||||
this._fireOnSelectionChange(start, end, hasSelection);
|
||||
}
|
||||
}
|
||||
|
||||
private _fireOnSelectionChange(start: [number, number] | undefined, end: [number, number] | undefined, hasSelection: boolean): void {
|
||||
this._oldSelectionStart = start;
|
||||
this._oldSelectionEnd = end;
|
||||
this._oldHasSelection = hasSelection;
|
||||
this._onSelectionChange.fire();
|
||||
}
|
||||
|
||||
private _onBufferActivate(e: {activeBuffer: IBuffer, inactiveBuffer: IBuffer}): void {
|
||||
this.clearSelection();
|
||||
// Only adjust the selection on trim, shiftElements is rarely used (only in
|
||||
@@ -727,6 +762,13 @@ export class SelectionService extends Disposable implements ISelectionService {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
public rightClickSelect(ev: MouseEvent): void {
|
||||
if (!this._isClickInSelection(ev)) {
|
||||
this._selectWordAtCursor(ev);
|
||||
this._fireEventIfSelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets positional information for the word at the coordinated specified.
|
||||
* @param coords The coordinates to get the word at.
|
||||
|
||||
@@ -91,8 +91,7 @@ export interface ISelectionService {
|
||||
selectAll(): void;
|
||||
selectLines(start: number, end: number): void;
|
||||
clearSelection(): void;
|
||||
isClickInSelection(event: MouseEvent): boolean;
|
||||
selectWordAtCursor(event: MouseEvent): void;
|
||||
rightClickSelect(event: MouseEvent): void;
|
||||
shouldColumnSelect(event: KeyboardEvent | MouseEvent): boolean;
|
||||
shouldForceSelection(event: MouseEvent): boolean;
|
||||
refresh(isLinuxMouseSelection?: boolean): void;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015",
|
||||
"es2016.Array.Include"
|
||||
],
|
||||
"outDir": "../../out",
|
||||
"types": [
|
||||
|
||||
@@ -158,6 +158,7 @@ export class CircularList<T> implements ICircularList<T> {
|
||||
this._array[this._getCyclicIndex(i)] = this._array[this._getCyclicIndex(i + deleteCount)];
|
||||
}
|
||||
this._length -= deleteCount;
|
||||
this.onDeleteEmitter.fire({index: start, amount: deleteCount});
|
||||
}
|
||||
|
||||
// Add items
|
||||
@@ -167,6 +168,9 @@ export class CircularList<T> implements ICircularList<T> {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
this._array[this._getCyclicIndex(start + i)] = items[i];
|
||||
}
|
||||
if (items.length) {
|
||||
this.onInsertEmitter.fire({index: start, amount: items.length});
|
||||
}
|
||||
|
||||
// Adjust length as needed
|
||||
if (this._length + items.length > this._maxLength) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
import { clone } from 'common/Clone';
|
||||
|
||||
describe('clone', () => {
|
||||
@@ -124,6 +124,6 @@ describe('clone', () => {
|
||||
|
||||
test.a.b.c = test;
|
||||
|
||||
expect(() => clone(test)).to.not.throw();
|
||||
assert.doesNotThrow(() => clone(test));
|
||||
});
|
||||
});
|
||||
|
||||
+151
-53
@@ -3,9 +3,9 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
import { InputHandler } from 'common/InputHandler';
|
||||
import { IBufferLine, IAttributeData } from 'common/Types';
|
||||
import { IBufferLine, IAttributeData, IAnsiColorChangeEvent } from 'common/Types';
|
||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { Attributes, UnderlineStyle } from 'common/buffer/Constants';
|
||||
@@ -40,6 +40,7 @@ class TestInputHandler extends InputHandler {
|
||||
public get curAttrData(): IAttributeData { return (this as any)._curAttrData; }
|
||||
public get windowTitleStack(): string[] { return this._windowTitleStack; }
|
||||
public get iconNameStack(): string[] { return this._iconNameStack; }
|
||||
public parseAnsiColorChange(data: string): IAnsiColorChangeEvent | null { return this._parseAnsiColorChange(data); }
|
||||
}
|
||||
|
||||
describe('InputHandler', () => {
|
||||
@@ -135,7 +136,16 @@ describe('InputHandler', () => {
|
||||
|
||||
it('insertChars', function(): void {
|
||||
const bufferService = new MockBufferService(80, 30);
|
||||
const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
|
||||
const inputHandler = new TestInputHandler(
|
||||
bufferService,
|
||||
new MockCharsetService(),
|
||||
new MockCoreService(),
|
||||
new MockDirtyRowService(),
|
||||
new MockLogService(),
|
||||
new MockOptionsService(),
|
||||
new MockCoreMouseService(),
|
||||
new MockUnicodeService()
|
||||
);
|
||||
|
||||
// insert some data in first and second line
|
||||
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
|
||||
@@ -143,36 +153,45 @@ describe('InputHandler', () => {
|
||||
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
|
||||
inputHandler.parse('1234567890');
|
||||
const line1: IBufferLine = bufferService.buffer.lines.get(0)!;
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '1234567890');
|
||||
|
||||
// insert one char from params = [0]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.insertChars(Params.fromArray([0]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 123456789');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' 123456789');
|
||||
|
||||
// insert one char from params = [1]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.insertChars(Params.fromArray([1]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 12345678');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' 12345678');
|
||||
|
||||
// insert two chars from params = [2]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.insertChars(Params.fromArray([2]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 123456');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' 123456');
|
||||
|
||||
// insert 10 chars from params = [10]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.insertChars(Params.fromArray([10]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' ');
|
||||
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a'));
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' ');
|
||||
assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a'));
|
||||
});
|
||||
it('deleteChars', function(): void {
|
||||
const bufferService = new MockBufferService(80, 30);
|
||||
const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
|
||||
const inputHandler = new TestInputHandler(
|
||||
bufferService,
|
||||
new MockCharsetService(),
|
||||
new MockCoreService(),
|
||||
new MockDirtyRowService(),
|
||||
new MockLogService(),
|
||||
new MockOptionsService(),
|
||||
new MockCoreMouseService(),
|
||||
new MockUnicodeService()
|
||||
);
|
||||
|
||||
// insert some data in first and second line
|
||||
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
|
||||
@@ -180,39 +199,49 @@ describe('InputHandler', () => {
|
||||
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
|
||||
inputHandler.parse('1234567890');
|
||||
const line1: IBufferLine = bufferService.buffer.lines.get(0)!;
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '1234567890');
|
||||
|
||||
// delete one char from params = [0]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.deleteChars(Params.fromArray([0]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '234567890 ');
|
||||
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '234567890');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '234567890 ');
|
||||
assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a') + '234567890');
|
||||
|
||||
// insert one char from params = [1]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.deleteChars(Params.fromArray([1]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '34567890 ');
|
||||
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '34567890');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '34567890 ');
|
||||
assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a') + '34567890');
|
||||
|
||||
// insert two chars from params = [2]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.deleteChars(Params.fromArray([2]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '567890 ');
|
||||
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '567890');
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '567890 ');
|
||||
assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a') + '567890');
|
||||
|
||||
|
||||
// insert 10 chars from params = [10]
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.deleteChars(Params.fromArray([10]));
|
||||
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' ');
|
||||
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a'));
|
||||
assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' ');
|
||||
assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a'));
|
||||
});
|
||||
it('eraseInLine', function(): void {
|
||||
const bufferService = new MockBufferService(80, 30);
|
||||
const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
|
||||
const inputHandler = new TestInputHandler(
|
||||
bufferService,
|
||||
new MockCharsetService(),
|
||||
new MockCoreService(),
|
||||
new MockDirtyRowService(),
|
||||
new MockLogService(),
|
||||
new MockOptionsService(),
|
||||
new MockCoreMouseService(),
|
||||
new MockUnicodeService()
|
||||
);
|
||||
|
||||
// fill 6 lines to test 3 different states
|
||||
inputHandler.parse(Array(bufferService.cols + 1).join('a'));
|
||||
@@ -223,24 +252,33 @@ describe('InputHandler', () => {
|
||||
bufferService.buffer.y = 0;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.eraseInLine(Params.fromArray([0]));
|
||||
expect(bufferService.buffer.lines.get(0)!.translateToString(false)).equals(Array(71).join('a') + ' ');
|
||||
assert.equal(bufferService.buffer.lines.get(0)!.translateToString(false), Array(71).join('a') + ' ');
|
||||
|
||||
// params[1] - left erase
|
||||
bufferService.buffer.y = 1;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.eraseInLine(Params.fromArray([1]));
|
||||
expect(bufferService.buffer.lines.get(1)!.translateToString(false)).equals(Array(71).join(' ') + ' aaaaaaaaa');
|
||||
assert.equal(bufferService.buffer.lines.get(1)!.translateToString(false), Array(71).join(' ') + ' aaaaaaaaa');
|
||||
|
||||
// params[1] - left erase
|
||||
bufferService.buffer.y = 2;
|
||||
bufferService.buffer.x = 70;
|
||||
inputHandler.eraseInLine(Params.fromArray([2]));
|
||||
expect(bufferService.buffer.lines.get(2)!.translateToString(false)).equals(Array(bufferService.cols + 1).join(' '));
|
||||
assert.equal(bufferService.buffer.lines.get(2)!.translateToString(false), Array(bufferService.cols + 1).join(' '));
|
||||
|
||||
});
|
||||
it('eraseInDisplay', function(): void {
|
||||
const bufferService = new MockBufferService(80, 7);
|
||||
const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
|
||||
const inputHandler = new TestInputHandler(
|
||||
bufferService,
|
||||
new MockCharsetService(),
|
||||
new MockCoreService(),
|
||||
new MockDirtyRowService(),
|
||||
new MockLogService(),
|
||||
new MockOptionsService(),
|
||||
new MockCoreMouseService(),
|
||||
new MockUnicodeService()
|
||||
);
|
||||
|
||||
// fill display with a's
|
||||
for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
|
||||
@@ -249,7 +287,7 @@ describe('InputHandler', () => {
|
||||
bufferService.buffer.y = 5;
|
||||
bufferService.buffer.x = 40;
|
||||
inputHandler.eraseInDisplay(Params.fromArray([0]));
|
||||
expect(termContent(bufferService, false)).eql([
|
||||
assert.deepEqual(termContent(bufferService, false), [
|
||||
Array(bufferService.cols + 1).join('a'),
|
||||
Array(bufferService.cols + 1).join('a'),
|
||||
Array(bufferService.cols + 1).join('a'),
|
||||
@@ -258,7 +296,7 @@ describe('InputHandler', () => {
|
||||
Array(40 + 1).join('a') + Array(bufferService.cols - 40 + 1).join(' '),
|
||||
Array(bufferService.cols + 1).join(' ')
|
||||
]);
|
||||
expect(termContent(bufferService, true)).eql([
|
||||
assert.deepEqual(termContent(bufferService, true), [
|
||||
Array(bufferService.cols + 1).join('a'),
|
||||
Array(bufferService.cols + 1).join('a'),
|
||||
Array(bufferService.cols + 1).join('a'),
|
||||
@@ -277,7 +315,7 @@ describe('InputHandler', () => {
|
||||
bufferService.buffer.y = 5;
|
||||
bufferService.buffer.x = 40;
|
||||
inputHandler.eraseInDisplay(Params.fromArray([1]));
|
||||
expect(termContent(bufferService, false)).eql([
|
||||
assert.deepEqual(termContent(bufferService, false), [
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
@@ -286,7 +324,7 @@ describe('InputHandler', () => {
|
||||
Array(41 + 1).join(' ') + Array(bufferService.cols - 41 + 1).join('a'),
|
||||
Array(bufferService.cols + 1).join('a')
|
||||
]);
|
||||
expect(termContent(bufferService, true)).eql([
|
||||
assert.deepEqual(termContent(bufferService, true), [
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
@@ -305,7 +343,7 @@ describe('InputHandler', () => {
|
||||
bufferService.buffer.y = 5;
|
||||
bufferService.buffer.x = 40;
|
||||
inputHandler.eraseInDisplay(Params.fromArray([2]));
|
||||
expect(termContent(bufferService, false)).eql([
|
||||
assert.deepEqual(termContent(bufferService, false), [
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
@@ -314,7 +352,7 @@ describe('InputHandler', () => {
|
||||
Array(bufferService.cols + 1).join(' '),
|
||||
Array(bufferService.cols + 1).join(' ')
|
||||
]);
|
||||
expect(termContent(bufferService, true)).eql([
|
||||
assert.deepEqual(termContent(bufferService, true), [
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
@@ -333,11 +371,11 @@ describe('InputHandler', () => {
|
||||
|
||||
// params[1] left and above with wrap
|
||||
// confirm precondition that line 2 is wrapped
|
||||
expect(bufferService.buffer.lines.get(2)!.isWrapped).true;
|
||||
assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true);
|
||||
bufferService.buffer.y = 2;
|
||||
bufferService.buffer.x = 40;
|
||||
inputHandler.eraseInDisplay(Params.fromArray([1]));
|
||||
expect(bufferService.buffer.lines.get(2)!.isWrapped).false;
|
||||
assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, false);
|
||||
|
||||
// reset and add a wrapped line
|
||||
bufferService.buffer.y = 0;
|
||||
@@ -348,16 +386,25 @@ describe('InputHandler', () => {
|
||||
|
||||
// params[1] left and above with wrap
|
||||
// confirm precondition that line 2 is wrapped
|
||||
expect(bufferService.buffer.lines.get(2)!.isWrapped).true;
|
||||
assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true);
|
||||
bufferService.buffer.y = 1;
|
||||
bufferService.buffer.x = 90; // Cursor is beyond last column
|
||||
inputHandler.eraseInDisplay(Params.fromArray([1]));
|
||||
expect(bufferService.buffer.lines.get(2)!.isWrapped).false;
|
||||
assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, false);
|
||||
});
|
||||
});
|
||||
describe('print', () => {
|
||||
it('should not cause an infinite loop (regression test)', () => {
|
||||
const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService());
|
||||
const inputHandler = new TestInputHandler(
|
||||
new MockBufferService(80, 30),
|
||||
new MockCharsetService(),
|
||||
new MockCoreService(),
|
||||
new MockDirtyRowService(),
|
||||
new MockLogService(),
|
||||
new MockOptionsService(),
|
||||
new MockCoreMouseService(),
|
||||
new MockUnicodeService()
|
||||
);
|
||||
const container = new Uint32Array(10);
|
||||
container[0] = 0x200B;
|
||||
inputHandler.print(container, 0, 1);
|
||||
@@ -382,48 +429,48 @@ describe('InputHandler', () => {
|
||||
});
|
||||
it('should handle DECSET/DECRST 47 (alt screen buffer)', () => {
|
||||
handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('');
|
||||
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(0, true), '');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(1, true), ' TEST');
|
||||
// Text color of 'TEST' should be red
|
||||
expect((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor())).to.equal(1);
|
||||
assert.equal((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor()), 1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1047 (alt screen buffer)', () => {
|
||||
handler.parse('\x1b[?1047h\r\n\x1b[31mJUNK\x1b[?1047lTEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('');
|
||||
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(0, true), '');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(1, true), ' TEST');
|
||||
// Text color of 'TEST' should be red
|
||||
expect((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor())).to.equal(1);
|
||||
assert.equal((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor()), 1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1048 (alt screen cursor)', () => {
|
||||
handler.parse('\x1b[?1048h\r\n\x1b[31mJUNK\x1b[?1048lTEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('JUNK');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(0, true), 'TEST');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(1, true), 'JUNK');
|
||||
// Text color of 'TEST' should be default
|
||||
expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
|
||||
assert.equal(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg, DEFAULT_ATTR_DATA.fg);
|
||||
// Text color of 'JUNK' should be red
|
||||
expect((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor())).to.equal(1);
|
||||
assert.equal((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor()), 1);
|
||||
});
|
||||
it('should handle DECSET/DECRST 1049 (alt screen buffer+cursor)', () => {
|
||||
handler.parse('\x1b[?1049h\r\n\x1b[31mJUNK\x1b[?1049lTEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(0, true), 'TEST');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(1, true), '');
|
||||
// Text color of 'TEST' should be default
|
||||
expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
|
||||
assert.equal(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg, 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(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(0, true), 'TEST');
|
||||
// Text color of 'TEST' should be default
|
||||
expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
|
||||
assert.equal(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg, DEFAULT_ATTR_DATA.fg);
|
||||
handler.parse('\x1b[?1049h\x1b[uTEST');
|
||||
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('TEST');
|
||||
assert.equal(bufferService.buffer.translateBufferLineToString(1, true), 'TEST');
|
||||
// Text color of 'TEST' should be red
|
||||
expect((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor())).to.equal(1);
|
||||
assert.equal((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor()), 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(bufferService.buffer.lines.get(20)!.loadCell(10, new CellData()).getBgColor()).to.equal(2);
|
||||
assert.equal(bufferService.buffer.lines.get(20)!.loadCell(10, new CellData()).getBgColor(), 2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1692,4 +1739,55 @@ describe('InputHandler', () => {
|
||||
assert.equal(coreService.decPrivateModes.origin, false);
|
||||
});
|
||||
});
|
||||
describe('OSC', () => {
|
||||
it('4: should parse correct Ansi color change data', () => {
|
||||
// this is testing a private method
|
||||
const event = inputHandler.parseAnsiColorChange('19;rgb:a1/b2/c3');
|
||||
|
||||
assert.isNotNull(event);
|
||||
assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 });
|
||||
}),
|
||||
it('4: should ignore incorrect Ansi color change data', () => {
|
||||
// this is testing a private method
|
||||
assert.isNull(inputHandler.parseAnsiColorChange('17;rgb:a/b/c'));
|
||||
assert.isNull(inputHandler.parseAnsiColorChange('17;rgb:#aabbcc'));
|
||||
assert.isNull(inputHandler.parseAnsiColorChange('17;rgba:aa/bb/cc'));
|
||||
assert.isNull(inputHandler.parseAnsiColorChange('rgb:aa/bb/cc'));
|
||||
});
|
||||
it('4: should parse a list of Ansi color changes', () => {
|
||||
// this is testing a private method
|
||||
const event = inputHandler.parseAnsiColorChange('19;rgb:a1/b2/c3;17;rgb:00/11/22;255;rgb:01/ef/2d');
|
||||
|
||||
assert.isNotNull(event);
|
||||
assert.equal(event!.colors.length, 3);
|
||||
assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 });
|
||||
assert.deepEqual(event!.colors[1], { colorIndex: 17, red: 0x00, green: 0x11, blue: 0x22 });
|
||||
assert.deepEqual(event!.colors[2], { colorIndex: 255, red: 0x01, green: 0xef, blue: 0x2d });
|
||||
});
|
||||
it('4: should ignore incorrect colors in a list of Ansi color changes', () => {
|
||||
// this is testing a private method
|
||||
const event = inputHandler.parseAnsiColorChange('19;rgb:a1/b2/c3;17;rgb:WR/ON/G;255;rgb:01/ef/2d');
|
||||
|
||||
assert.equal(event!.colors.length, 2);
|
||||
assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 });
|
||||
assert.deepEqual(event!.colors[1], { colorIndex: 255, red: 0x01, green: 0xef, blue: 0x2d });
|
||||
});
|
||||
it('4: should be case insensitive when parsing Ansi color changes', () => {
|
||||
// this is testing a private method
|
||||
const event = inputHandler.parseAnsiColorChange('19;rGb:A1/b2/C3');
|
||||
|
||||
assert.equal(event!.colors.length, 1);
|
||||
assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 });
|
||||
});
|
||||
it('4: should fire event on Ansi color change', (done) => {
|
||||
inputHandler.onAnsiColorChange(e => {
|
||||
assert.isNotNull(e);
|
||||
assert.isNotNull(e!.colors);
|
||||
assert.deepEqual(e!.colors[0], { colorIndex: 17, red: 0x1a, green: 0x2b, blue: 0x3c });
|
||||
assert.deepEqual(e!.colors[1], { colorIndex: 12, red: 0x11, green: 0x22, blue: 0x33 });
|
||||
done();
|
||||
});
|
||||
inputHandler.parse('\x1b]4;17;rgb:1a/2b/3c;12;rgb:11/22/33\x1b\\');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+286
-172
File diff suppressed because it is too large
Load Diff
+3
-12
@@ -17,23 +17,14 @@ const isNode = (typeof navigator === 'undefined') ? true : false;
|
||||
const userAgent = (isNode) ? 'node' : navigator.userAgent;
|
||||
const platform = (isNode) ? 'node' : navigator.platform;
|
||||
|
||||
export const isFirefox = !!~userAgent.indexOf('Firefox');
|
||||
export const isFirefox = userAgent.includes('Firefox');
|
||||
export const isSafari = /^((?!chrome|android).)*safari/i.test(userAgent);
|
||||
|
||||
// Find the users platform. We use this to interpret the meta key
|
||||
// and ISO third level shifts.
|
||||
// http://stackoverflow.com/q/19877924/577598
|
||||
export const isMac = contains(['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], platform);
|
||||
export const isMac = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'].includes(platform);
|
||||
export const isIpad = platform === 'iPad';
|
||||
export const isIphone = platform === 'iPhone';
|
||||
export const isWindows = contains(['Windows', 'Win16', 'Win32', 'WinCE'], platform);
|
||||
export const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].includes(platform);
|
||||
export const isLinux = platform.indexOf('Linux') >= 0;
|
||||
|
||||
/**
|
||||
* Return if the given array contains the given element
|
||||
* @param arr The array to search for the given element.
|
||||
* @param el The element to look for into the array
|
||||
*/
|
||||
function contains(arr: any[], el: any): boolean {
|
||||
return arr.indexOf(el) >= 0;
|
||||
}
|
||||
|
||||
Vendored
+16
@@ -183,6 +183,7 @@ export interface IMarker extends IDisposable {
|
||||
readonly id: number;
|
||||
readonly isDisposed: boolean;
|
||||
readonly line: number;
|
||||
onDispose: IEvent<void>;
|
||||
}
|
||||
export interface IModes {
|
||||
insertMode: boolean;
|
||||
@@ -327,6 +328,20 @@ export interface IWindowOptions {
|
||||
setWinLines?: boolean;
|
||||
}
|
||||
|
||||
export interface IAnsiColorChangeEventColor {
|
||||
colorIndex: number;
|
||||
red: number;
|
||||
green: number;
|
||||
blue: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event fired for OSC 4 command - to change ANSI color based on its index.
|
||||
*/
|
||||
export interface IAnsiColorChangeEvent {
|
||||
colors: IAnsiColorChangeEventColor[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the parser and handles actions generated by the parser.
|
||||
*/
|
||||
@@ -388,6 +403,7 @@ export interface IInputHandler {
|
||||
/** CSI ' ~ */ deleteColumns(params: IParams): void;
|
||||
/** OSC 0
|
||||
OSC 2 */ setTitle(data: string): void;
|
||||
/** OSC 4 */ setAnsiColor(data: string): void;
|
||||
/** ESC E */ nextLine(): void;
|
||||
/** ESC = */ keypadApplicationMode(): void;
|
||||
/** ESC > */ keypadNumericMode(): void;
|
||||
|
||||
@@ -700,7 +700,7 @@ describe('Buffer', () => {
|
||||
}
|
||||
const wrappedLines: number[] = [];
|
||||
for (let i = 0; i < buffer.lines.length; i++) {
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -723,7 +723,7 @@ describe('Buffer', () => {
|
||||
}
|
||||
const wrappedLines: number[] = [];
|
||||
for (let i = 0; i < buffer.lines.length; i++) {
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -754,7 +754,7 @@ describe('Buffer', () => {
|
||||
}
|
||||
const wrappedLines: number[] = [];
|
||||
for (let i = 0; i < buffer.lines.length; i++) {
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -777,7 +777,7 @@ describe('Buffer', () => {
|
||||
}
|
||||
const wrappedLines: number[] = [];
|
||||
for (let i = 0; i < buffer.lines.length; i++) {
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -814,7 +814,7 @@ describe('Buffer', () => {
|
||||
}
|
||||
const wrappedLines: number[] = [];
|
||||
for (let i = 0; i < buffer.lines.length; i++) {
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -837,7 +837,7 @@ describe('Buffer', () => {
|
||||
}
|
||||
const wrappedLines: number[] = [];
|
||||
for (let i = 0; i < buffer.lines.length; i++) {
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -891,7 +891,7 @@ describe('Buffer', () => {
|
||||
}
|
||||
const wrappedLines = [1, 3, 5];
|
||||
for (let i = 0; i < buffer.lines.length; i++) {
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -917,7 +917,7 @@ describe('Buffer', () => {
|
||||
}
|
||||
const wrappedLines = [1, 3, 5];
|
||||
for (let i = 0; i < buffer.lines.length; i++) {
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -950,7 +950,7 @@ describe('Buffer', () => {
|
||||
}
|
||||
const wrappedLines = [11, 13, 15];
|
||||
for (let i = 0; i < buffer.lines.length; i++) {
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -975,7 +975,7 @@ describe('Buffer', () => {
|
||||
}
|
||||
const wrappedLines = [11, 13, 15];
|
||||
for (let i = 0; i < buffer.lines.length; i++) {
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1014,7 +1014,7 @@ describe('Buffer', () => {
|
||||
}
|
||||
const wrappedLines = [8, 10, 12];
|
||||
for (let i = 0; i < buffer.lines.length; i++) {
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1040,7 +1040,7 @@ describe('Buffer', () => {
|
||||
}
|
||||
const wrappedLines = [8, 10, 12];
|
||||
for (let i = 0; i < buffer.lines.length; i++) {
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
|
||||
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1085,6 +1085,20 @@ describe('Buffer', () => {
|
||||
assert.equal(marker.isDisposed, true);
|
||||
assert.equal(buffer.markers.length, 0);
|
||||
});
|
||||
it('should call onDispose', () => {
|
||||
const eventStack: string[] = [];
|
||||
buffer = new Buffer(true, new MockOptionsService({ scrollback: 0 }), bufferService);
|
||||
buffer.fillViewportRows();
|
||||
assert.equal(buffer.markers.length, 0);
|
||||
const marker = buffer.addMarker(0);
|
||||
marker.onDispose(() => eventStack.push('disposed'));
|
||||
assert.equal(marker.isDisposed, false);
|
||||
assert.equal(buffer.markers.length, 1);
|
||||
buffer.lines.onTrimEmitter.fire(1);
|
||||
assert.equal(marker.isDisposed, true);
|
||||
assert.equal(buffer.markers.length, 0);
|
||||
assert.deepEqual(eventStack, ['disposed']);
|
||||
});
|
||||
});
|
||||
|
||||
describe ('translateBufferLineToString', () => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR, Content,
|
||||
import { BufferLine } from 'common/buffer//BufferLine';
|
||||
import { CellData } from 'common/buffer/CellData';
|
||||
import { CharData, IBufferLine } from '../Types';
|
||||
import { assert, expect } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
import { AttributeData } from 'common/buffer/AttributeData';
|
||||
|
||||
|
||||
@@ -151,20 +151,20 @@ describe('CellData', () => {
|
||||
describe('BufferLine', function(): void {
|
||||
it('ctor', function(): void {
|
||||
let line: IBufferLine = new TestBufferLine(0);
|
||||
expect(line.length).equals(0);
|
||||
expect(line.isWrapped).equals(false);
|
||||
assert.equal(line.length, 0);
|
||||
assert.equal(line.isWrapped, false);
|
||||
line = new TestBufferLine(10);
|
||||
expect(line.length).equals(10);
|
||||
expect(line.loadCell(0, new CellData()).getAsCharData()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
expect(line.isWrapped).equals(false);
|
||||
assert.equal(line.length, 10);
|
||||
assert.deepEqual(line.loadCell(0, new CellData()).getAsCharData(), [0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
assert.equal(line.isWrapped, false);
|
||||
line = new TestBufferLine(10, undefined, true);
|
||||
expect(line.length).equals(10);
|
||||
expect(line.loadCell(0, new CellData()).getAsCharData()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
expect(line.isWrapped).equals(true);
|
||||
assert.equal(line.length, 10);
|
||||
assert.deepEqual(line.loadCell(0, new CellData()).getAsCharData(), [0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
assert.equal(line.isWrapped, true);
|
||||
line = new TestBufferLine(10, CellData.fromCharData([123, 'a', 456, 'a'.charCodeAt(0)]), true);
|
||||
expect(line.length).equals(10);
|
||||
expect(line.loadCell(0, new CellData()).getAsCharData()).eql([123, 'a', 456, 'a'.charCodeAt(0)]);
|
||||
expect(line.isWrapped).equals(true);
|
||||
assert.equal(line.length, 10);
|
||||
assert.deepEqual(line.loadCell(0, new CellData()).getAsCharData(), [123, 'a', 456, 'a'.charCodeAt(0)]);
|
||||
assert.equal(line.isWrapped, true);
|
||||
});
|
||||
it('insertCells', function(): void {
|
||||
const line = new TestBufferLine(3);
|
||||
@@ -172,7 +172,7 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
expect(line.toArray()).eql([
|
||||
assert.deepEqual(line.toArray(), [
|
||||
[1, 'a', 0, 'a'.charCodeAt(0)],
|
||||
[4, 'd', 0, 'd'.charCodeAt(0)],
|
||||
[4, 'd', 0, 'd'.charCodeAt(0)]
|
||||
@@ -186,7 +186,7 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
expect(line.toArray()).eql([
|
||||
assert.deepEqual(line.toArray(), [
|
||||
[1, 'a', 0, 'a'.charCodeAt(0)],
|
||||
[4, 'd', 0, 'd'.charCodeAt(0)],
|
||||
[5, 'e', 0, 'e'.charCodeAt(0)],
|
||||
@@ -202,7 +202,7 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
expect(line.toArray()).eql([
|
||||
assert.deepEqual(line.toArray(), [
|
||||
[1, 'a', 0, 'a'.charCodeAt(0)],
|
||||
[2, 'b', 0, 'b'.charCodeAt(0)],
|
||||
[6, 'f', 0, 'f'.charCodeAt(0)],
|
||||
@@ -218,7 +218,7 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
expect(line.toArray()).eql([
|
||||
assert.deepEqual(line.toArray(), [
|
||||
[123, 'z', 0, 'z'.charCodeAt(0)],
|
||||
[123, 'z', 0, 'z'.charCodeAt(0)],
|
||||
[123, 'z', 0, 'z'.charCodeAt(0)],
|
||||
@@ -234,9 +234,9 @@ describe('BufferLine', function(): void {
|
||||
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();
|
||||
expect(TestBufferLine.prototype.toArray.apply(line2)).eql(line.toArray());
|
||||
expect(line2.length).equals(line.length);
|
||||
expect(line2.isWrapped).equals(line.isWrapped);
|
||||
assert.deepEqual(TestBufferLine.prototype.toArray.apply(line2), line.toArray());
|
||||
assert.equal(line2.length, line.length);
|
||||
assert.equal(line2.isWrapped, line.isWrapped);
|
||||
});
|
||||
it('copyFrom', function(): void {
|
||||
const line = new TestBufferLine(5);
|
||||
@@ -247,92 +247,92 @@ describe('BufferLine', function(): void {
|
||||
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);
|
||||
expect(line2.toArray()).eql(line.toArray());
|
||||
expect(line2.length).equals(line.length);
|
||||
expect(line2.isWrapped).equals(line.isWrapped);
|
||||
assert.deepEqual(line2.toArray(), line.toArray());
|
||||
assert.equal(line2.length, line.length);
|
||||
assert.equal(line2.isWrapped, line.isWrapped);
|
||||
});
|
||||
it('should support combining chars', 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, CellData.fromCharData([1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]));
|
||||
expect(line.toArray()).eql([[1, 'e\u0301', 0, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]]);
|
||||
assert.deepEqual(line.toArray(), [[1, 'e\u0301', 0, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]]);
|
||||
const line2 = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, '\u0301'.charCodeAt(0)]), true);
|
||||
line2.copyFrom(line);
|
||||
expect(line2.toArray()).eql(line.toArray());
|
||||
assert.deepEqual(line2.toArray(), line.toArray());
|
||||
const line3 = line.clone();
|
||||
expect(TestBufferLine.prototype.toArray.apply(line3)).eql(line.toArray());
|
||||
assert.deepEqual(TestBufferLine.prototype.toArray.apply(line3), line.toArray());
|
||||
});
|
||||
describe('resize', function(): void {
|
||||
it('enlarge(false)', function(): void {
|
||||
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)]));
|
||||
expect(line.toArray()).eql((Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
assert.deepEqual(line.toArray(), (Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('enlarge(true)', function(): void {
|
||||
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)]));
|
||||
expect(line.toArray()).eql((Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
assert.deepEqual(line.toArray(), (Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink(true) - should apply new size', function(): void {
|
||||
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)]));
|
||||
expect(line.toArray()).eql((Array(5) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
assert.deepEqual(line.toArray(), (Array(5) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink to 0 length', function(): void {
|
||||
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)]));
|
||||
expect(line.toArray()).eql((Array(0) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
assert.deepEqual(line.toArray(), (Array(0) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('should remove combining data on replaced cells after shrinking then enlarging', () => {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false);
|
||||
line.set(2, [ 0, '😁', 1, '😁'.charCodeAt(0) ]);
|
||||
line.set(9, [ 0, '😁', 1, '😁'.charCodeAt(0) ]);
|
||||
expect(line.translateToString()).eql('aa😁aaaaaa😁');
|
||||
expect(Object.keys(line.combined).length).eql(2);
|
||||
assert.equal(line.translateToString(), 'aa😁aaaaaa😁');
|
||||
assert.equal(Object.keys(line.combined).length, 2);
|
||||
line.resize(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
expect(line.translateToString()).eql('aa😁aa');
|
||||
assert.equal(line.translateToString(), 'aa😁aa');
|
||||
line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
expect(line.translateToString()).eql('aa😁aaaaaaa');
|
||||
expect(Object.keys(line.combined).length).eql(1);
|
||||
assert.equal(line.translateToString(), 'aa😁aaaaaaa');
|
||||
assert.equal(Object.keys(line.combined).length, 1);
|
||||
});
|
||||
});
|
||||
describe('getTrimLength', function(): void {
|
||||
it('empty line', function(): void {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
expect(line.getTrimmedLength()).equal(0);
|
||||
assert.equal(line.getTrimmedLength(), 0);
|
||||
});
|
||||
it('ASCII', function(): void {
|
||||
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)]));
|
||||
expect(line.getTrimmedLength()).equal(3);
|
||||
assert.equal(line.getTrimmedLength(), 3);
|
||||
});
|
||||
it('surrogate', function(): void {
|
||||
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)]));
|
||||
expect(line.getTrimmedLength()).equal(3);
|
||||
assert.equal(line.getTrimmedLength(), 3);
|
||||
});
|
||||
it('combining', function(): void {
|
||||
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)]));
|
||||
expect(line.getTrimmedLength()).equal(3);
|
||||
assert.equal(line.getTrimmedLength(), 3);
|
||||
});
|
||||
it('fullwidth', function(): void {
|
||||
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, 0]));
|
||||
expect(line.getTrimmedLength()).equal(4); // also counts null cell after fullwidth
|
||||
assert.equal(line.getTrimmedLength(), 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, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
expect(line.translateToString(false)).equal(' ');
|
||||
expect(line.translateToString(true)).equal('');
|
||||
assert.equal(line.translateToString(false), ' ');
|
||||
assert.equal(line.translateToString(true), '');
|
||||
});
|
||||
it('ASCII', function(): void {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
@@ -340,14 +340,14 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
expect(line.translateToString(false)).equal('a a aa ');
|
||||
expect(line.translateToString(true)).equal('a a aa');
|
||||
expect(line.translateToString(false, 0, 5)).equal('a a a');
|
||||
expect(line.translateToString(false, 0, 4)).equal('a a ');
|
||||
expect(line.translateToString(false, 0, 3)).equal('a a');
|
||||
expect(line.translateToString(true, 0, 5)).equal('a a a');
|
||||
expect(line.translateToString(true, 0, 4)).equal('a a ');
|
||||
expect(line.translateToString(true, 0, 3)).equal('a a');
|
||||
assert.equal(line.translateToString(false), 'a a aa ');
|
||||
assert.equal(line.translateToString(true), 'a a aa');
|
||||
assert.equal(line.translateToString(false, 0, 5), 'a a a');
|
||||
assert.equal(line.translateToString(false, 0, 4), 'a a ');
|
||||
assert.equal(line.translateToString(false, 0, 3), 'a a');
|
||||
assert.equal(line.translateToString(true, 0, 5), 'a a a');
|
||||
assert.equal(line.translateToString(true, 0, 4), 'a a ');
|
||||
assert.equal(line.translateToString(true, 0, 3), 'a a');
|
||||
|
||||
});
|
||||
it('surrogate', function(): void {
|
||||
@@ -356,14 +356,14 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
expect(line.translateToString(false)).equal('a 𝄞 𝄞𝄞 ');
|
||||
expect(line.translateToString(true)).equal('a 𝄞 𝄞𝄞');
|
||||
expect(line.translateToString(false, 0, 5)).equal('a 𝄞 𝄞');
|
||||
expect(line.translateToString(false, 0, 4)).equal('a 𝄞 ');
|
||||
expect(line.translateToString(false, 0, 3)).equal('a 𝄞');
|
||||
expect(line.translateToString(true, 0, 5)).equal('a 𝄞 𝄞');
|
||||
expect(line.translateToString(true, 0, 4)).equal('a 𝄞 ');
|
||||
expect(line.translateToString(true, 0, 3)).equal('a 𝄞');
|
||||
assert.equal(line.translateToString(false), 'a 𝄞 𝄞𝄞 ');
|
||||
assert.equal(line.translateToString(true), 'a 𝄞 𝄞𝄞');
|
||||
assert.equal(line.translateToString(false, 0, 5), 'a 𝄞 𝄞');
|
||||
assert.equal(line.translateToString(false, 0, 4), 'a 𝄞 ');
|
||||
assert.equal(line.translateToString(false, 0, 3), 'a 𝄞');
|
||||
assert.equal(line.translateToString(true, 0, 5), 'a 𝄞 𝄞');
|
||||
assert.equal(line.translateToString(true, 0, 4), 'a 𝄞 ');
|
||||
assert.equal(line.translateToString(true, 0, 3), 'a 𝄞');
|
||||
});
|
||||
it('combining', function(): void {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
@@ -371,14 +371,14 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
expect(line.translateToString(false)).equal('a e\u0301 e\u0301e\u0301 ');
|
||||
expect(line.translateToString(true)).equal('a e\u0301 e\u0301e\u0301');
|
||||
expect(line.translateToString(false, 0, 5)).equal('a e\u0301 e\u0301');
|
||||
expect(line.translateToString(false, 0, 4)).equal('a e\u0301 ');
|
||||
expect(line.translateToString(false, 0, 3)).equal('a e\u0301');
|
||||
expect(line.translateToString(true, 0, 5)).equal('a e\u0301 e\u0301');
|
||||
expect(line.translateToString(true, 0, 4)).equal('a e\u0301 ');
|
||||
expect(line.translateToString(true, 0, 3)).equal('a e\u0301');
|
||||
assert.equal(line.translateToString(false), 'a e\u0301 e\u0301e\u0301 ');
|
||||
assert.equal(line.translateToString(true), 'a e\u0301 e\u0301e\u0301');
|
||||
assert.equal(line.translateToString(false, 0, 5), 'a e\u0301 e\u0301');
|
||||
assert.equal(line.translateToString(false, 0, 4), 'a e\u0301 ');
|
||||
assert.equal(line.translateToString(false, 0, 3), 'a e\u0301');
|
||||
assert.equal(line.translateToString(true, 0, 5), 'a e\u0301 e\u0301');
|
||||
assert.equal(line.translateToString(true, 0, 4), 'a e\u0301 ');
|
||||
assert.equal(line.translateToString(true, 0, 3), 'a e\u0301');
|
||||
});
|
||||
it('fullwidth', function(): void {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
@@ -389,20 +389,20 @@ describe('BufferLine', function(): void {
|
||||
line.setCell(6, CellData.fromCharData([0, '', 0, 0]));
|
||||
line.setCell(7, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)]));
|
||||
line.setCell(8, CellData.fromCharData([0, '', 0, 0]));
|
||||
expect(line.translateToString(false)).equal('a 1 11 ');
|
||||
expect(line.translateToString(true)).equal('a 1 11');
|
||||
expect(line.translateToString(false, 0, 7)).equal('a 1 1');
|
||||
expect(line.translateToString(false, 0, 6)).equal('a 1 1');
|
||||
expect(line.translateToString(false, 0, 5)).equal('a 1 ');
|
||||
expect(line.translateToString(false, 0, 4)).equal('a 1');
|
||||
expect(line.translateToString(false, 0, 3)).equal('a 1');
|
||||
expect(line.translateToString(false, 0, 2)).equal('a ');
|
||||
expect(line.translateToString(true, 0, 7)).equal('a 1 1');
|
||||
expect(line.translateToString(true, 0, 6)).equal('a 1 1');
|
||||
expect(line.translateToString(true, 0, 5)).equal('a 1 ');
|
||||
expect(line.translateToString(true, 0, 4)).equal('a 1');
|
||||
expect(line.translateToString(true, 0, 3)).equal('a 1');
|
||||
expect(line.translateToString(true, 0, 2)).equal('a ');
|
||||
assert.equal(line.translateToString(false), 'a 1 11 ');
|
||||
assert.equal(line.translateToString(true), 'a 1 11');
|
||||
assert.equal(line.translateToString(false, 0, 7), 'a 1 1');
|
||||
assert.equal(line.translateToString(false, 0, 6), 'a 1 1');
|
||||
assert.equal(line.translateToString(false, 0, 5), 'a 1 ');
|
||||
assert.equal(line.translateToString(false, 0, 4), 'a 1');
|
||||
assert.equal(line.translateToString(false, 0, 3), 'a 1');
|
||||
assert.equal(line.translateToString(false, 0, 2), 'a ');
|
||||
assert.equal(line.translateToString(true, 0, 7), 'a 1 1');
|
||||
assert.equal(line.translateToString(true, 0, 6), 'a 1 1');
|
||||
assert.equal(line.translateToString(true, 0, 5), 'a 1 ');
|
||||
assert.equal(line.translateToString(true, 0, 4), 'a 1');
|
||||
assert.equal(line.translateToString(true, 0, 3), 'a 1');
|
||||
assert.equal(line.translateToString(true, 0, 2), 'a ');
|
||||
});
|
||||
it('space at end', function(): void {
|
||||
const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false);
|
||||
@@ -411,21 +411,21 @@ describe('BufferLine', function(): void {
|
||||
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)]));
|
||||
expect(line.translateToString(false)).equal('a a aa ');
|
||||
expect(line.translateToString(true)).equal('a a aa ');
|
||||
assert.equal(line.translateToString(false), 'a a aa ');
|
||||
assert.equal(line.translateToString(true), 'a a aa ');
|
||||
});
|
||||
it('should always return some sane value', 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, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false);
|
||||
expect(line.translateToString(false)).equal(' ');
|
||||
expect(line.translateToString(true)).equal('');
|
||||
assert.equal(line.translateToString(false), ' ');
|
||||
assert.equal(line.translateToString(true), '');
|
||||
});
|
||||
it('should work with endCol=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)]));
|
||||
expect(line.translateToString(true, 0, 0)).equal('');
|
||||
assert.equal(line.translateToString(true, 0, 0), '');
|
||||
});
|
||||
});
|
||||
describe('addCharToCell', () => {
|
||||
|
||||
@@ -15,10 +15,9 @@ import { Disposable } from 'common/Lifecycle';
|
||||
* provides also utilities for working with them.
|
||||
*/
|
||||
export class BufferSet extends Disposable implements IBufferSet {
|
||||
private _normal: Buffer;
|
||||
private _alt: Buffer;
|
||||
private _activeBuffer: Buffer;
|
||||
|
||||
private _normal!: Buffer;
|
||||
private _alt!: Buffer;
|
||||
private _activeBuffer!: Buffer;
|
||||
|
||||
private _onBufferActivate = this.register(new EventEmitter<{activeBuffer: IBuffer, inactiveBuffer: IBuffer}>());
|
||||
public get onBufferActivate(): IEvent<{activeBuffer: IBuffer, inactiveBuffer: IBuffer}> { return this._onBufferActivate.event; }
|
||||
@@ -28,17 +27,20 @@ export class BufferSet extends Disposable implements IBufferSet {
|
||||
* @param _terminal - The terminal the BufferSet will belong to
|
||||
*/
|
||||
constructor(
|
||||
optionsService: IOptionsService,
|
||||
bufferService: IBufferService
|
||||
private readonly _optionsService: IOptionsService,
|
||||
private readonly _bufferService: IBufferService
|
||||
) {
|
||||
super();
|
||||
this.reset();
|
||||
}
|
||||
|
||||
this._normal = new Buffer(true, optionsService, bufferService);
|
||||
public reset(): void {
|
||||
this._normal = new Buffer(true, this._optionsService, this._bufferService);
|
||||
this._normal.fillViewportRows();
|
||||
|
||||
// The alt buffer should never have scrollback.
|
||||
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
this._alt = new Buffer(false, optionsService, bufferService);
|
||||
this._alt = new Buffer(false, this._optionsService, this._bufferService);
|
||||
this._activeBuffer = this._normal;
|
||||
|
||||
this.setupTabStops();
|
||||
|
||||
@@ -32,5 +32,6 @@ export class Marker extends Disposable implements IMarker {
|
||||
this.line = -1;
|
||||
// Emit before super.dispose such that dispose listeners get a change to react
|
||||
this._onDispose.fire();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -56,6 +56,7 @@ export interface IBufferSet extends IDisposable {
|
||||
|
||||
activateNormalBuffer(): void;
|
||||
activateAltBuffer(fillAttr?: IAttributeData): void;
|
||||
reset(): void;
|
||||
resize(newCols: number, newRows: number): void;
|
||||
setupTabStops(i?: number): void;
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ export function evaluateKeyboardEvent(
|
||||
const keyCode = ev.ctrlKey ? ev.keyCode - 64 : ev.keyCode + 32;
|
||||
result.key = C0.ESC + String.fromCharCode(keyCode);
|
||||
}
|
||||
} else if (isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) {
|
||||
} else if (isMac && !ev.altKey && !ev.ctrlKey && !ev.shiftKey && ev.metaKey) {
|
||||
if (ev.keyCode === 65) { // cmd + a
|
||||
result.type = KeyboardResultType.SELECT_ALL;
|
||||
}
|
||||
|
||||
@@ -58,8 +58,8 @@ describe('text encodings', () => {
|
||||
const decoder = new StringToUtf32();
|
||||
const target = new Uint32Array(5);
|
||||
for (let i = 0; i < 65536; ++i) {
|
||||
// skip surrogate pairs
|
||||
if (i >= 0xD800 && i <= 0xDFFF) {
|
||||
// skip surrogate pairs and a BOM
|
||||
if ((i >= 0xD800 && i <= 0xDFFF) || i === 0xFEFF) {
|
||||
continue;
|
||||
}
|
||||
const length = decoder.decode(String.fromCharCode(i), target);
|
||||
@@ -84,6 +84,14 @@ describe('text encodings', () => {
|
||||
decoder.clear();
|
||||
}
|
||||
});
|
||||
|
||||
it('0xFEFF(BOM)', () => {
|
||||
const decoder = new StringToUtf32();
|
||||
const target = new Uint32Array(5);
|
||||
const length = decoder.decode(String.fromCharCode(0xFEFF), target);
|
||||
assert.equal(length, 0);
|
||||
decoder.clear();
|
||||
});
|
||||
});
|
||||
|
||||
it('test strings', () => {
|
||||
@@ -118,8 +126,8 @@ describe('text encodings', () => {
|
||||
const decoder = new Utf8ToUtf32();
|
||||
const target = new Uint32Array(5);
|
||||
for (let i = 0; i < 65536; ++i) {
|
||||
// skip surrogate pairs
|
||||
if (i >= 0xD800 && i <= 0xDFFF) {
|
||||
// skip surrogate pairs and a BOM
|
||||
if ((i >= 0xD800 && i <= 0xDFFF) || i === 0xFEFF) {
|
||||
continue;
|
||||
}
|
||||
const utf8Data = fromByteString(encode(String.fromCharCode(i)));
|
||||
@@ -142,6 +150,15 @@ describe('text encodings', () => {
|
||||
decoder.clear();
|
||||
}
|
||||
});
|
||||
|
||||
it('0xFEFF(BOM)', () => {
|
||||
const decoder = new Utf8ToUtf32();
|
||||
const target = new Uint32Array(5);
|
||||
const utf8Data = fromByteString(encode(String.fromCharCode(0xFEFF)));
|
||||
const length = decoder.decode(utf8Data, target);
|
||||
assert.equal(length, 0);
|
||||
decoder.clear();
|
||||
});
|
||||
});
|
||||
|
||||
it('test strings', () => {
|
||||
@@ -215,6 +232,19 @@ describe('text encodings', () => {
|
||||
}
|
||||
assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
|
||||
});
|
||||
|
||||
it('BOMs (3 byte sequences) - advance by 2', () => {
|
||||
const decoder = new Utf8ToUtf32();
|
||||
const target = new Uint32Array(5);
|
||||
const utf8Data = fromByteString('\xef\xbb\xbf\xef\xbb\xbf');
|
||||
let decoded = '';
|
||||
for (let i = 0; i < utf8Data.length; i += 2) {
|
||||
const written = decoder.decode(utf8Data.slice(i, i + 2), target);
|
||||
decoded += toString(target, written);
|
||||
}
|
||||
assert.equal(decoded, '');
|
||||
});
|
||||
|
||||
it('test break after 3 bytes - issue #2495', () => {
|
||||
const decoder = new Utf8ToUtf32();
|
||||
const target = new Uint32Array(5);
|
||||
|
||||
@@ -105,6 +105,10 @@ export class StringToUtf32 {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (code === 0xFEFF) {
|
||||
// BOM
|
||||
continue;
|
||||
}
|
||||
target[size++] = code;
|
||||
}
|
||||
return size;
|
||||
@@ -188,8 +192,8 @@ export class Utf8ToUtf32 {
|
||||
target[size++] = cp;
|
||||
}
|
||||
} else if (type === 3) {
|
||||
if (cp < 0x0800 || (cp >= 0xD800 && cp <= 0xDFFF)) {
|
||||
// illegal codepoint
|
||||
if (cp < 0x0800 || (cp >= 0xD800 && cp <= 0xDFFF) || cp === 0xFEFF) {
|
||||
// illegal codepoint or BOM
|
||||
} else {
|
||||
target[size++] = cp;
|
||||
}
|
||||
@@ -286,8 +290,8 @@ export class Utf8ToUtf32 {
|
||||
continue;
|
||||
}
|
||||
codepoint = (byte1 & 0x0F) << 12 | (byte2 & 0x3F) << 6 | (byte3 & 0x3F);
|
||||
if (codepoint < 0x0800 || (codepoint >= 0xD800 && codepoint <= 0xDFFF)) {
|
||||
// illegal codepoint, no i-- here
|
||||
if (codepoint < 0x0800 || (codepoint >= 0xD800 && codepoint <= 0xDFFF) || codepoint === 0xFEFF) {
|
||||
// illegal codepoint or BOM, no i-- here
|
||||
continue;
|
||||
}
|
||||
target[size++] = codepoint;
|
||||
|
||||
@@ -161,7 +161,7 @@ it('wcwidth should match all values from the old implementation', function(): vo
|
||||
// ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766
|
||||
// we are only interested in 2 LSBs, cut off higher bits
|
||||
// ==> n = n & 3 e.g. 000000000000000000000000000000XX
|
||||
return function (num: number): number {
|
||||
return (num: number): number => {
|
||||
num = num | 0; // get asm.js like optimization under V8
|
||||
if (num < 32) {
|
||||
return control | 0;
|
||||
|
||||
@@ -61,11 +61,12 @@ class TestHandler implements IDcsHandler {
|
||||
public put(data: Uint32Array, start: number, end: number): void {
|
||||
this.output.push([this.msg, 'PUT', utf32ToString(data, start, end)]);
|
||||
}
|
||||
public unhook(success: boolean): void | boolean {
|
||||
public unhook(success: boolean): boolean {
|
||||
this.output.push([this.msg, 'UNHOOK', success]);
|
||||
if (this.returnFalse) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +85,7 @@ describe('DcsParser', () => {
|
||||
});
|
||||
describe('handler registration', () => {
|
||||
it('setDcsHandler', () => {
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th'));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th'));
|
||||
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -100,7 +101,7 @@ describe('DcsParser', () => {
|
||||
]);
|
||||
});
|
||||
it('clearDcsHandler', () => {
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th'));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th'));
|
||||
parser.clearHandler(identifier({intermediates: '+', final: 'p'}));
|
||||
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('Here comes');
|
||||
@@ -117,8 +118,8 @@ describe('DcsParser', () => {
|
||||
]);
|
||||
});
|
||||
it('addDcsHandler', () => {
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
|
||||
parser.addHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2'));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2'));
|
||||
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -137,8 +138,8 @@ describe('DcsParser', () => {
|
||||
]);
|
||||
});
|
||||
it('addDcsHandler with return false', () => {
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
|
||||
parser.addHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2', true));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2', true));
|
||||
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -157,8 +158,8 @@ describe('DcsParser', () => {
|
||||
]);
|
||||
});
|
||||
it('dispose handlers', () => {
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
|
||||
const dispo = parser.addHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2', true));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
|
||||
const dispo = parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2', true));
|
||||
dispo.dispose();
|
||||
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('Here comes');
|
||||
@@ -176,7 +177,7 @@ describe('DcsParser', () => {
|
||||
});
|
||||
describe('DcsHandlerFactory', () => {
|
||||
it('should be called once on end(true)', () => {
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push([params.toArray(), data])));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push([params.toArray(), data]); return true; }));
|
||||
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -186,7 +187,7 @@ describe('DcsParser', () => {
|
||||
assert.deepEqual(reports, [[[1, 2, 3], 'Here comes the mouse!']]);
|
||||
});
|
||||
it('should not be called on end(false)', () => {
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push([params.toArray(), data])));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push([params.toArray(), data]); return true; }));
|
||||
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -196,8 +197,8 @@ describe('DcsParser', () => {
|
||||
assert.deepEqual(reports, []);
|
||||
});
|
||||
it('should be disposable', () => {
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push(['one', params.toArray(), data])));
|
||||
const dispo = parser.addHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push(['two', params.toArray(), data])));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['one', params.toArray(), data]); return true; }));
|
||||
const dispo = parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['two', params.toArray(), data]); return true; }));
|
||||
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -215,8 +216,8 @@ describe('DcsParser', () => {
|
||||
assert.deepEqual(reports, [['two', [1, 2, 3], 'Here comes the mouse!'], ['one', [1, 2, 3], 'some other data']]);
|
||||
});
|
||||
it('should respect return false', () => {
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push(['one', params.toArray(), data])));
|
||||
parser.addHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['two', params.toArray(), data]); return false; }));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['one', params.toArray(), data]); return true; }));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['two', params.toArray(), data]); return false; }));
|
||||
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -227,7 +228,7 @@ describe('DcsParser', () => {
|
||||
});
|
||||
it('should work up to payload limit', function(): void {
|
||||
this.timeout(10000);
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push([params.toArray(), data])));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push([params.toArray(), data]); return true; }));
|
||||
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
const data = toUtf32('A'.repeat(1000));
|
||||
for (let i = 0; i < PAYLOAD_LIMIT; i += 1000) {
|
||||
@@ -238,7 +239,7 @@ describe('DcsParser', () => {
|
||||
});
|
||||
it('should abort for payload limit +1', function(): void {
|
||||
this.timeout(10000);
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push([params.toArray(), data])));
|
||||
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push([params.toArray(), data]); return true; }));
|
||||
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('A'.repeat(1000));
|
||||
for (let i = 0; i < PAYLOAD_LIMIT; i += 1000) {
|
||||
|
||||
@@ -20,9 +20,10 @@ export class DcsParser implements IDcsParser {
|
||||
public dispose(): void {
|
||||
this._handlers = Object.create(null);
|
||||
this._handlerFb = () => {};
|
||||
this._active = EMPTY_HANDLERS;
|
||||
}
|
||||
|
||||
public addHandler(ident: number, handler: IDcsHandler): IDisposable {
|
||||
public registerHandler(ident: number, handler: IDcsHandler): IDisposable {
|
||||
if (this._handlers[ident] === undefined) {
|
||||
this._handlers[ident] = [];
|
||||
}
|
||||
@@ -38,10 +39,6 @@ export class DcsParser implements IDcsParser {
|
||||
};
|
||||
}
|
||||
|
||||
public setHandler(ident: number, handler: IDcsHandler): void {
|
||||
this._handlers[ident] = [handler];
|
||||
}
|
||||
|
||||
public clearHandler(ident: number): void {
|
||||
if (this._handlers[ident]) delete this._handlers[ident];
|
||||
}
|
||||
@@ -88,7 +85,7 @@ export class DcsParser implements IDcsParser {
|
||||
} else {
|
||||
let j = this._active.length - 1;
|
||||
for (; j >= 0; j--) {
|
||||
if (this._active[j].unhook(success) !== false) {
|
||||
if (this._active[j].unhook(success)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -103,19 +100,27 @@ export class DcsParser implements IDcsParser {
|
||||
}
|
||||
}
|
||||
|
||||
// predefine empty params as [0] (ZDM)
|
||||
const EMPTY_PARAMS = new Params();
|
||||
EMPTY_PARAMS.addParam(0);
|
||||
|
||||
/**
|
||||
* Convenient class to create a DCS handler from a single callback function.
|
||||
* Note: The payload is currently limited to 50 MB (hardcoded).
|
||||
*/
|
||||
export class DcsHandler implements IDcsHandler {
|
||||
private _data = '';
|
||||
private _params: IParams | undefined;
|
||||
private _params: IParams = EMPTY_PARAMS;
|
||||
private _hitLimit: boolean = false;
|
||||
|
||||
constructor(private _handler: (data: string, params: IParams) => any) {}
|
||||
constructor(private _handler: (data: string, params: IParams) => boolean) {}
|
||||
|
||||
public hook(params: IParams): void {
|
||||
this._params = params.clone();
|
||||
// since we need to preserve params until `unhook`, we have to clone it
|
||||
// (only borrowed from parser and spans multiple parser states)
|
||||
// perf optimization:
|
||||
// clone only, if we have non empty params, otherwise stick with default
|
||||
this._params = (params.length > 1 || params.params[0]) ? params.clone() : EMPTY_PARAMS;
|
||||
this._data = '';
|
||||
this._hitLimit = false;
|
||||
}
|
||||
@@ -131,14 +136,14 @@ export class DcsHandler implements IDcsHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public unhook(success: boolean): any {
|
||||
let ret;
|
||||
public unhook(success: boolean): boolean {
|
||||
let ret = false;
|
||||
if (this._hitLimit) {
|
||||
ret = false;
|
||||
} else if (success) {
|
||||
ret = this._handler(this._data, this._params || new Params());
|
||||
ret = this._handler(this._data, this._params);
|
||||
}
|
||||
this._params = undefined;
|
||||
this._params = EMPTY_PARAMS;
|
||||
this._data = '';
|
||||
this._hitLimit = false;
|
||||
return ret;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -280,7 +280,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._errorHandler = this._errorHandlerFb;
|
||||
|
||||
// swallow 7bit ST (ESC+\)
|
||||
this.setEscHandler({final: '\\'}, () => {});
|
||||
this.registerEscHandler({final: '\\'}, () => true);
|
||||
}
|
||||
|
||||
protected _identifier(id: IFunctionIdentifier, finalRange: number[] = [0x40, 0x7e]): number {
|
||||
@@ -344,7 +344,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._printHandler = this._printHandlerFb;
|
||||
}
|
||||
|
||||
public addEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): IDisposable {
|
||||
public registerEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): IDisposable {
|
||||
const ident = this._identifier(id, [0x30, 0x7e]);
|
||||
if (this._escHandlers[ident] === undefined) {
|
||||
this._escHandlers[ident] = [];
|
||||
@@ -360,9 +360,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
}
|
||||
};
|
||||
}
|
||||
public setEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): void {
|
||||
this._escHandlers[this._identifier(id, [0x30, 0x7e])] = [handler];
|
||||
}
|
||||
public clearEscHandler(id: IFunctionIdentifier): void {
|
||||
if (this._escHandlers[this._identifier(id, [0x30, 0x7e])]) delete this._escHandlers[this._identifier(id, [0x30, 0x7e])];
|
||||
}
|
||||
@@ -380,7 +377,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._executeHandlerFb = handler;
|
||||
}
|
||||
|
||||
public addCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): IDisposable {
|
||||
public registerCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): IDisposable {
|
||||
const ident = this._identifier(id);
|
||||
if (this._csiHandlers[ident] === undefined) {
|
||||
this._csiHandlers[ident] = [];
|
||||
@@ -396,9 +393,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
}
|
||||
};
|
||||
}
|
||||
public setCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): void {
|
||||
this._csiHandlers[this._identifier(id)] = [handler];
|
||||
}
|
||||
public clearCsiHandler(id: IFunctionIdentifier): void {
|
||||
if (this._csiHandlers[this._identifier(id)]) delete this._csiHandlers[this._identifier(id)];
|
||||
}
|
||||
@@ -406,11 +400,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._csiHandlerFb = callback;
|
||||
}
|
||||
|
||||
public addDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): IDisposable {
|
||||
return this._dcsParser.addHandler(this._identifier(id), handler);
|
||||
}
|
||||
public setDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): void {
|
||||
this._dcsParser.setHandler(this._identifier(id), handler);
|
||||
public registerDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): IDisposable {
|
||||
return this._dcsParser.registerHandler(this._identifier(id), handler);
|
||||
}
|
||||
public clearDcsHandler(id: IFunctionIdentifier): void {
|
||||
this._dcsParser.clearHandler(this._identifier(id));
|
||||
@@ -419,11 +410,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._dcsParser.setHandlerFallback(handler);
|
||||
}
|
||||
|
||||
public addOscHandler(ident: number, handler: IOscHandler): IDisposable {
|
||||
return this._oscParser.addHandler(ident, handler);
|
||||
}
|
||||
public setOscHandler(ident: number, handler: IOscHandler): void {
|
||||
this._oscParser.setHandler(ident, handler);
|
||||
public registerOscHandler(ident: number, handler: IOscHandler): IDisposable {
|
||||
return this._oscParser.registerHandler(ident, handler);
|
||||
}
|
||||
public clearOscHandler(ident: number): void {
|
||||
this._oscParser.clearHandler(ident);
|
||||
@@ -533,8 +521,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
const handlers = this._csiHandlers[collect << 8 | code];
|
||||
let j = handlers ? handlers.length - 1 : -1;
|
||||
for (; j >= 0; j--) {
|
||||
// undefined or true means success and to stop bubbling
|
||||
if (handlers[j](params) !== false) {
|
||||
// true means success and to stop bubbling
|
||||
if (handlers[j](params)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -567,8 +555,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
const handlersEsc = this._escHandlers[collect << 8 | code];
|
||||
let jj = handlersEsc ? handlersEsc.length - 1 : -1;
|
||||
for (; jj >= 0; jj--) {
|
||||
// undefined or true means success and to stop bubbling
|
||||
if (handlersEsc[jj]() !== false) {
|
||||
// true means success and to stop bubbling
|
||||
if (handlersEsc[jj]()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -610,7 +598,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
case ParserAction.OSC_PUT:
|
||||
// inner loop: 0x20 (SP) included, 0x7F (DEL) included
|
||||
for (let j = i + 1; ; j++) {
|
||||
if (j >= length || (code = data[j]) < 0x20 || (code > 0x7f && code <= 0x9f)) {
|
||||
if (j >= length || (code = data[j]) < 0x20 || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
|
||||
osc.put(data, i, j);
|
||||
i = j - 1;
|
||||
break;
|
||||
|
||||
@@ -23,11 +23,12 @@ class TestHandler implements IOscHandler {
|
||||
public put(data: Uint32Array, start: number, end: number): void {
|
||||
this.output.push([this.msg, this.id, 'PUT', utf32ToString(data, start, end)]);
|
||||
}
|
||||
public end(success: boolean): void | boolean {
|
||||
public end(success: boolean): boolean {
|
||||
this.output.push([this.msg, this.id, 'END', success]);
|
||||
if (this.returnFalse) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +79,7 @@ describe('OscParser', () => {
|
||||
});
|
||||
describe('handler registration', () => {
|
||||
it('setOscHandler', () => {
|
||||
parser.setHandler(1234, new TestHandler(1234, reports, 'th'));
|
||||
parser.registerHandler(1234, new TestHandler(1234, reports, 'th'));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -94,7 +95,7 @@ describe('OscParser', () => {
|
||||
]);
|
||||
});
|
||||
it('clearOscHandler', () => {
|
||||
parser.setHandler(1234, new TestHandler(1234, reports, 'th'));
|
||||
parser.registerHandler(1234, new TestHandler(1234, reports, 'th'));
|
||||
parser.clearHandler(1234);
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
@@ -111,8 +112,8 @@ describe('OscParser', () => {
|
||||
]);
|
||||
});
|
||||
it('addOscHandler', () => {
|
||||
parser.setHandler(1234, new TestHandler(1234, reports, 'th1'));
|
||||
parser.addHandler(1234, new TestHandler(1234, reports, 'th2'));
|
||||
parser.registerHandler(1234, new TestHandler(1234, reports, 'th1'));
|
||||
parser.registerHandler(1234, new TestHandler(1234, reports, 'th2'));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -131,8 +132,8 @@ describe('OscParser', () => {
|
||||
]);
|
||||
});
|
||||
it('addOscHandler with return false', () => {
|
||||
parser.setHandler(1234, new TestHandler(1234, reports, 'th1'));
|
||||
parser.addHandler(1234, new TestHandler(1234, reports, 'th2', true));
|
||||
parser.registerHandler(1234, new TestHandler(1234, reports, 'th1'));
|
||||
parser.registerHandler(1234, new TestHandler(1234, reports, 'th2', true));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -151,8 +152,8 @@ describe('OscParser', () => {
|
||||
]);
|
||||
});
|
||||
it('dispose handlers', () => {
|
||||
parser.setHandler(1234, new TestHandler(1234, reports, 'th1'));
|
||||
const dispo = parser.addHandler(1234, new TestHandler(1234, reports, 'th2', true));
|
||||
parser.registerHandler(1234, new TestHandler(1234, reports, 'th1'));
|
||||
const dispo = parser.registerHandler(1234, new TestHandler(1234, reports, 'th2', true));
|
||||
dispo.dispose();
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
@@ -170,7 +171,7 @@ describe('OscParser', () => {
|
||||
});
|
||||
describe('OscHandlerFactory', () => {
|
||||
it('should be called once on end(true)', () => {
|
||||
parser.setHandler(1234, new OscHandler(data => reports.push([1234, data])));
|
||||
parser.registerHandler(1234, new OscHandler(data => { reports.push([1234, data]); return true; }));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -180,7 +181,7 @@ describe('OscParser', () => {
|
||||
assert.deepEqual(reports, [[1234, 'Here comes the mouse!']]);
|
||||
});
|
||||
it('should not be called on end(false)', () => {
|
||||
parser.setHandler(1234, new OscHandler(data => reports.push([1234, data])));
|
||||
parser.registerHandler(1234, new OscHandler(data => { reports.push([1234, data]); return true; }));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -190,8 +191,8 @@ describe('OscParser', () => {
|
||||
assert.deepEqual(reports, []);
|
||||
});
|
||||
it('should be disposable', () => {
|
||||
parser.setHandler(1234, new OscHandler(data => reports.push(['one', data])));
|
||||
const dispo = parser.addHandler(1234, new OscHandler(data => reports.push(['two', data])));
|
||||
parser.registerHandler(1234, new OscHandler(data => { reports.push(['one', data]); return true; }));
|
||||
const dispo = parser.registerHandler(1234, new OscHandler(data => { reports.push(['two', data]); return true; }));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -209,8 +210,8 @@ describe('OscParser', () => {
|
||||
assert.deepEqual(reports, [['two', 'Here comes the mouse!'], ['one', 'some other data']]);
|
||||
});
|
||||
it('should respect return false', () => {
|
||||
parser.setHandler(1234, new OscHandler(data => reports.push(['one', data])));
|
||||
parser.addHandler(1234, new OscHandler(data => { reports.push(['two', data]); return false; }));
|
||||
parser.registerHandler(1234, new OscHandler(data => { reports.push(['one', data]); return true; }));
|
||||
parser.registerHandler(1234, new OscHandler(data => { reports.push(['two', data]); return false; }));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -221,7 +222,7 @@ describe('OscParser', () => {
|
||||
});
|
||||
it('should work up to payload limit', function(): void {
|
||||
this.timeout(10000);
|
||||
parser.setHandler(1234, new OscHandler(data => reports.push([1234, data])));
|
||||
parser.registerHandler(1234, new OscHandler(data => { reports.push([1234, data]); return true; }));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -234,7 +235,7 @@ describe('OscParser', () => {
|
||||
});
|
||||
it('should abort for payload limit +1', function(): void {
|
||||
this.timeout(10000);
|
||||
parser.setHandler(1234, new OscHandler(data => reports.push([1234, data])));
|
||||
parser.registerHandler(1234, new OscHandler(data => { reports.push([1234, data]); return true; }));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;');
|
||||
parser.put(data, 0, data.length);
|
||||
|
||||
@@ -8,14 +8,16 @@ import { OscState, PAYLOAD_LIMIT } from 'common/parser/Constants';
|
||||
import { utf32ToString } from 'common/input/TextDecoder';
|
||||
import { IDisposable } from 'common/Types';
|
||||
|
||||
const EMPTY_HANDLERS: IOscHandler[] = [];
|
||||
|
||||
export class OscParser implements IOscParser {
|
||||
private _state = OscState.START;
|
||||
private _active = EMPTY_HANDLERS;
|
||||
private _id = -1;
|
||||
private _handlers: IHandlerCollection<IOscHandler> = Object.create(null);
|
||||
private _handlerFb: OscFallbackHandlerType = () => { };
|
||||
|
||||
public addHandler(ident: number, handler: IOscHandler): IDisposable {
|
||||
public registerHandler(ident: number, handler: IOscHandler): IDisposable {
|
||||
if (this._handlers[ident] === undefined) {
|
||||
this._handlers[ident] = [];
|
||||
}
|
||||
@@ -30,9 +32,6 @@ export class OscParser implements IOscParser {
|
||||
}
|
||||
};
|
||||
}
|
||||
public setHandler(ident: number, handler: IOscHandler): void {
|
||||
this._handlers[ident] = [handler];
|
||||
}
|
||||
public clearHandler(ident: number): void {
|
||||
if (this._handlers[ident]) delete this._handlers[ident];
|
||||
}
|
||||
@@ -43,6 +42,7 @@ export class OscParser implements IOscParser {
|
||||
public dispose(): void {
|
||||
this._handlers = Object.create(null);
|
||||
this._handlerFb = () => {};
|
||||
this._active = EMPTY_HANDLERS;
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
@@ -50,28 +50,28 @@ export class OscParser implements IOscParser {
|
||||
if (this._state === OscState.PAYLOAD) {
|
||||
this.end(false);
|
||||
}
|
||||
this._active = EMPTY_HANDLERS;
|
||||
this._id = -1;
|
||||
this._state = OscState.START;
|
||||
}
|
||||
|
||||
private _start(): void {
|
||||
const handlers = this._handlers[this._id];
|
||||
if (!handlers) {
|
||||
this._active = this._handlers[this._id] || EMPTY_HANDLERS;
|
||||
if (!this._active.length) {
|
||||
this._handlerFb(this._id, 'START');
|
||||
} else {
|
||||
for (let j = handlers.length - 1; j >= 0; j--) {
|
||||
handlers[j].start();
|
||||
for (let j = this._active.length - 1; j >= 0; j--) {
|
||||
this._active[j].start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _put(data: Uint32Array, start: number, end: number): void {
|
||||
const handlers = this._handlers[this._id];
|
||||
if (!handlers) {
|
||||
if (!this._active.length) {
|
||||
this._handlerFb(this._id, 'PUT', utf32ToString(data, start, end));
|
||||
} else {
|
||||
for (let j = handlers.length - 1; j >= 0; j--) {
|
||||
handlers[j].put(data, start, end);
|
||||
for (let j = this._active.length - 1; j >= 0; j--) {
|
||||
this._active[j].put(data, start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,20 +80,19 @@ export class OscParser implements IOscParser {
|
||||
// other than the old code we always have to call .end
|
||||
// to keep the bubbling we use `success` to indicate
|
||||
// whether a handler should execute
|
||||
const handlers = this._handlers[this._id];
|
||||
if (!handlers) {
|
||||
if (!this._active.length) {
|
||||
this._handlerFb(this._id, 'END', success);
|
||||
} else {
|
||||
let j = handlers.length - 1;
|
||||
let j = this._active.length - 1;
|
||||
for (; j >= 0; j--) {
|
||||
if (handlers[j].end(success) !== false) {
|
||||
if (this._active[j].end(success)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
j--;
|
||||
// cleanup left over handlers
|
||||
for (; j >= 0; j--) {
|
||||
handlers[j].end(false);
|
||||
this._active[j].end(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,7 +100,6 @@ export class OscParser implements IOscParser {
|
||||
public start(): void {
|
||||
// always reset leftover handlers
|
||||
this.reset();
|
||||
this._id = -1;
|
||||
this._state = OscState.ID;
|
||||
}
|
||||
|
||||
@@ -158,6 +156,7 @@ export class OscParser implements IOscParser {
|
||||
}
|
||||
this._end(success);
|
||||
}
|
||||
this._active = EMPTY_HANDLERS;
|
||||
this._id = -1;
|
||||
this._state = OscState.START;
|
||||
}
|
||||
@@ -171,7 +170,7 @@ export class OscHandler implements IOscHandler {
|
||||
private _data = '';
|
||||
private _hitLimit: boolean = false;
|
||||
|
||||
constructor(private _handler: (data: string) => any) {}
|
||||
constructor(private _handler: (data: string) => boolean) {}
|
||||
|
||||
public start(): void {
|
||||
this._data = '';
|
||||
@@ -189,8 +188,8 @@ export class OscHandler implements IOscHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public end(success: boolean): any {
|
||||
let ret;
|
||||
public end(success: boolean): boolean {
|
||||
let ret = false;
|
||||
if (this._hitLimit) {
|
||||
ret = false;
|
||||
} else if (success) {
|
||||
|
||||
Vendored
+10
-15
@@ -69,7 +69,7 @@ export interface IParsingState {
|
||||
* CSI handler types.
|
||||
* Note: `params` is borrowed.
|
||||
*/
|
||||
export type CsiHandlerType = (params: IParams) => boolean | void;
|
||||
export type CsiHandlerType = (params: IParams) => boolean;
|
||||
export type CsiFallbackHandlerType = (ident: number, params: IParams) => void;
|
||||
|
||||
/**
|
||||
@@ -93,20 +93,20 @@ export interface IDcsHandler {
|
||||
* execution of the command should depend on `success`.
|
||||
* To save memory also cleanup data structures here.
|
||||
*/
|
||||
unhook(success: boolean): void | boolean;
|
||||
unhook(success: boolean): boolean;
|
||||
}
|
||||
export type DcsFallbackHandlerType = (ident: number, action: 'HOOK' | 'PUT' | 'UNHOOK', payload?: any) => void;
|
||||
|
||||
/**
|
||||
* ESC handler types.
|
||||
*/
|
||||
export type EscHandlerType = () => boolean | void;
|
||||
export type EscHandlerType = () => boolean;
|
||||
export type EscFallbackHandlerType = (identifier: number) => void;
|
||||
|
||||
/**
|
||||
* EXECUTE handler types.
|
||||
*/
|
||||
export type ExecuteHandlerType = () => boolean | void;
|
||||
export type ExecuteHandlerType = () => boolean;
|
||||
export type ExecuteFallbackHandlerType = (ident: number) => void;
|
||||
|
||||
/**
|
||||
@@ -129,7 +129,7 @@ export interface IOscHandler {
|
||||
* execution of the command should depend on `success`.
|
||||
* To save memory also cleanup data structures here.
|
||||
*/
|
||||
end(success: boolean): void | boolean;
|
||||
end(success: boolean): boolean;
|
||||
}
|
||||
export type OscFallbackHandlerType = (ident: number, action: 'START' | 'PUT' | 'END', payload?: any) => void;
|
||||
|
||||
@@ -174,29 +174,25 @@ export interface IEscapeSequenceParser extends IDisposable {
|
||||
setPrintHandler(handler: PrintHandlerType): void;
|
||||
clearPrintHandler(): void;
|
||||
|
||||
setEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): void;
|
||||
registerEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): IDisposable;
|
||||
clearEscHandler(id: IFunctionIdentifier): void;
|
||||
setEscHandlerFallback(handler: EscFallbackHandlerType): void;
|
||||
addEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): IDisposable;
|
||||
|
||||
setExecuteHandler(flag: string, handler: ExecuteHandlerType): void;
|
||||
clearExecuteHandler(flag: string): void;
|
||||
setExecuteHandlerFallback(handler: ExecuteFallbackHandlerType): void;
|
||||
|
||||
setCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): void;
|
||||
registerCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): IDisposable;
|
||||
clearCsiHandler(id: IFunctionIdentifier): void;
|
||||
setCsiHandlerFallback(callback: CsiFallbackHandlerType): void;
|
||||
addCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): IDisposable;
|
||||
|
||||
setDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): void;
|
||||
registerDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): IDisposable;
|
||||
clearDcsHandler(id: IFunctionIdentifier): void;
|
||||
setDcsHandlerFallback(handler: DcsFallbackHandlerType): void;
|
||||
addDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): IDisposable;
|
||||
|
||||
setOscHandler(ident: number, handler: IOscHandler): void;
|
||||
registerOscHandler(ident: number, handler: IOscHandler): IDisposable;
|
||||
clearOscHandler(ident: number): void;
|
||||
setOscHandlerFallback(handler: OscFallbackHandlerType): void;
|
||||
addOscHandler(ident: number, handler: IOscHandler): IDisposable;
|
||||
|
||||
setErrorHandler(handler: (state: IParsingState) => IParsingState): void;
|
||||
clearErrorHandler(): void;
|
||||
@@ -209,8 +205,7 @@ export interface IEscapeSequenceParser extends IDisposable {
|
||||
*/
|
||||
export interface ISubParser<T, U> extends IDisposable {
|
||||
reset(): void;
|
||||
addHandler(ident: number, handler: T): IDisposable;
|
||||
setHandler(ident: number, handler: T): void;
|
||||
registerHandler(ident: number, handler: T): IDisposable;
|
||||
clearHandler(ident: number): void;
|
||||
setHandlerFallback(handler: U): void;
|
||||
put(data: Uint32Array, start: number, end: number): void;
|
||||
|
||||
@@ -49,8 +49,7 @@ export class BufferService extends Disposable implements IBufferService {
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this.buffers.dispose();
|
||||
this.buffers = new BufferSet(this._optionsService, this);
|
||||
this.buffers.reset();
|
||||
this.isUserScrolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
|
||||
windowOptions: {},
|
||||
windowsMode: false,
|
||||
wordSeparator: ' ()[]{}\',"`',
|
||||
|
||||
altClickMovesCursor: true,
|
||||
convertEol: false,
|
||||
termName: 'xterm',
|
||||
cancelEvents: false
|
||||
@@ -89,7 +89,7 @@ export class OptionsService implements IOptionsService {
|
||||
if (!(key in DEFAULT_OPTIONS)) {
|
||||
throw new Error('No option with key "' + key + '"');
|
||||
}
|
||||
if (CONSTRUCTOR_ONLY_OPTIONS.indexOf(key) !== -1) {
|
||||
if (CONSTRUCTOR_ONLY_OPTIONS.includes(key)) {
|
||||
throw new Error(`Option "${key}" can only be set in the constructor`);
|
||||
}
|
||||
if (this.options[key] === value) {
|
||||
@@ -123,7 +123,7 @@ export class OptionsService implements IOptionsService {
|
||||
// already valid numeric value
|
||||
break;
|
||||
}
|
||||
value = FONT_WEIGHT_OPTIONS.indexOf(value) !== -1 ? value : DEFAULT_OPTIONS[key];
|
||||
value = FONT_WEIGHT_OPTIONS.includes(value) ? value : DEFAULT_OPTIONS[key];
|
||||
break;
|
||||
case 'cursorWidth':
|
||||
value = Math.floor(value);
|
||||
|
||||
@@ -184,6 +184,7 @@ export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off';
|
||||
export type RendererType = 'dom' | 'canvas';
|
||||
|
||||
export interface IPartialTerminalOptions {
|
||||
altClickMovesCursor?: boolean;
|
||||
allowTransparency?: boolean;
|
||||
bellSound?: string;
|
||||
bellStyle?: 'none' | 'sound' /* | 'visual' | 'both' */;
|
||||
@@ -220,6 +221,7 @@ export interface IPartialTerminalOptions {
|
||||
export interface ITerminalOptions {
|
||||
allowProposedApi: boolean;
|
||||
allowTransparency: boolean;
|
||||
altClickMovesCursor: boolean;
|
||||
bellSound: string;
|
||||
bellStyle: 'none' | 'sound' /* | 'visual' | 'both' */;
|
||||
cols: number;
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"extends": "../tsconfig-library-base",
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"es2015"
|
||||
"es2015",
|
||||
"es2016.Array.Include"
|
||||
],
|
||||
"outDir": "../../out",
|
||||
"types": [
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { pollFor, openTerminal, getBrowserType } from './TestUtils';
|
||||
import { Browser, Page } from 'playwright-core';
|
||||
import { Browser, Page } from 'playwright';
|
||||
|
||||
const APP = 'http://127.0.0.1:3000/test';
|
||||
|
||||
@@ -16,7 +16,7 @@ const height = 600;
|
||||
describe('CharWidth Integration Tests', function(): void {
|
||||
before(async function(): Promise<any> {
|
||||
const browserType = getBrowserType();
|
||||
browser = await browserType.launch({ dumpio: true,
|
||||
browser = await browserType.launch({
|
||||
headless: process.argv.indexOf('--headless') !== -1
|
||||
});
|
||||
page = await (await browser.newContext()).newPage();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { pollFor, openTerminal, getBrowserType } from './TestUtils';
|
||||
import { Browser, Page } from 'playwright-core';
|
||||
import { Browser, Page } from 'playwright';
|
||||
import { IRenderDimensions } from 'browser/renderer/Types';
|
||||
|
||||
const APP = 'http://127.0.0.1:3000/test';
|
||||
@@ -21,7 +21,7 @@ describe('InputHandler Integration Tests', function(): void {
|
||||
before(async function(): Promise<any> {
|
||||
const browserType = getBrowserType();
|
||||
isChromium = browserType.name() === 'chromium';
|
||||
browser = await browserType.launch({ dumpio: true,
|
||||
browser = await browserType.launch({
|
||||
headless: process.argv.indexOf('--headless') !== -1
|
||||
});
|
||||
page = await (await browser.newContext()).newPage();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { pollFor, writeSync, openTerminal, getBrowserType } from './TestUtils';
|
||||
import { Browser, Page } from 'playwright-core';
|
||||
import { Browser, Page } from 'playwright';
|
||||
|
||||
const APP = 'http://127.0.0.1:3000/test';
|
||||
|
||||
@@ -81,8 +81,7 @@ async function wheelUp(): Promise<void> {
|
||||
x: self._x,
|
||||
y: self._y,
|
||||
deltaX: 0,
|
||||
deltaY: -10,
|
||||
modifiers: toModifiersMask(page.keyboard._modifiers())
|
||||
deltaY: -10
|
||||
});
|
||||
}
|
||||
async function wheelDown(): Promise<void> {
|
||||
@@ -92,8 +91,7 @@ async function wheelDown(): Promise<void> {
|
||||
x: self._x,
|
||||
y: self._y,
|
||||
deltaX: 0,
|
||||
deltaY: 10,
|
||||
modifiers: toModifiersMask(page.keyboard._modifiers())
|
||||
deltaY: 10
|
||||
});
|
||||
}
|
||||
|
||||
@@ -213,7 +211,7 @@ describe('Mouse Tracking Tests', async () => {
|
||||
const itMouse = isChromium ? it : it.skip;
|
||||
|
||||
before(async function(): Promise<void> {
|
||||
browser = await browserType.launch({ dumpio: true,
|
||||
browser = await browserType.launch({
|
||||
headless: process.argv.indexOf('--headless') !== -1
|
||||
});
|
||||
page = await (await browser.newContext()).newPage();
|
||||
|
||||
+20
-20
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { writeSync, openTerminal, getBrowserType } from './TestUtils';
|
||||
import { Browser, Page } from 'playwright-core';
|
||||
import { Browser, Page } from 'playwright';
|
||||
|
||||
const APP = 'http://127.0.0.1:3000/test';
|
||||
|
||||
@@ -17,7 +17,7 @@ const height = 600;
|
||||
describe('Parser Integration Tests', function(): void {
|
||||
before(async function(): Promise<any> {
|
||||
const browserType = getBrowserType();
|
||||
browser = await browserType.launch({ dumpio: true,
|
||||
browser = await browserType.launch({
|
||||
headless: process.argv.indexOf('--headless') !== -1
|
||||
});
|
||||
page = await (await browser.newContext()).newPage();
|
||||
@@ -32,80 +32,80 @@ describe('Parser Integration Tests', function(): void {
|
||||
it('should call custom CSI handler with js array params', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
const _customCsiHandlerParams = [];
|
||||
window._customCsiHandlerParams = [];
|
||||
const _customCsiHandler = window.term.parser.addCsiHandler({final: 'm'}, (params, collect) => {
|
||||
_customCsiHandlerParams.push(params);
|
||||
window._customCsiHandlerParams.push(params);
|
||||
return false;
|
||||
}, '');
|
||||
`);
|
||||
await writeSync(page, '\x1b[38;5;123mparams\x1b[38:2::50:100:150msubparams');
|
||||
assert.deepEqual(await page.evaluate(`(() => _customCsiHandlerParams)();`), [[38, 5, 123], [38, [2, -1, 50, 100, 150]]]);
|
||||
assert.deepEqual(await page.evaluate(`(() => window._customCsiHandlerParams)();`), [[38, 5, 123], [38, [2, -1, 50, 100, 150]]]);
|
||||
});
|
||||
});
|
||||
describe('addDcsHandler', () => {
|
||||
it('should respects return value', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
const _customDcsHandlerCallStack = [];
|
||||
window._customDcsHandlerCallStack = [];
|
||||
const _customDcsHandlerA = window.term.parser.addDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
_customDcsHandlerCallStack.push(['A', params, data]);
|
||||
window._customDcsHandlerCallStack.push(['A', params, data]);
|
||||
return false;
|
||||
});
|
||||
const _customDcsHandlerB = window.term.parser.addDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
_customDcsHandlerCallStack.push(['B', params, data]);
|
||||
window._customDcsHandlerCallStack.push(['B', params, data]);
|
||||
return true;
|
||||
});
|
||||
const _customDcsHandlerC = window.term.parser.addDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
_customDcsHandlerCallStack.push(['C', params, data]);
|
||||
window._customDcsHandlerCallStack.push(['C', params, data]);
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await writeSync(page, '\x1bP1;2+psome data\x1b\\\\');
|
||||
assert.deepEqual(await page.evaluate(`(() => _customDcsHandlerCallStack)();`), [['C', [1, 2], 'some data'], ['B', [1, 2], 'some data']]);
|
||||
assert.deepEqual(await page.evaluate(`(() => window._customDcsHandlerCallStack)();`), [['C', [1, 2], 'some data'], ['B', [1, 2], 'some data']]);
|
||||
});
|
||||
});
|
||||
describe('addEscHandler', () => {
|
||||
it('should respects return value', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
const _customEscHandlerCallStack = [];
|
||||
window._customEscHandlerCallStack = [];
|
||||
const _customEscHandlerA = window.term.parser.addEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
_customEscHandlerCallStack.push('A');
|
||||
window._customEscHandlerCallStack.push('A');
|
||||
return false;
|
||||
});
|
||||
const _customEscHandlerB = window.term.parser.addEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
_customEscHandlerCallStack.push('B');
|
||||
window._customEscHandlerCallStack.push('B');
|
||||
return true;
|
||||
});
|
||||
const _customEscHandlerC = window.term.parser.addEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
_customEscHandlerCallStack.push('C');
|
||||
window._customEscHandlerCallStack.push('C');
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await writeSync(page, '\x1b(B');
|
||||
assert.deepEqual(await page.evaluate(`(() => _customEscHandlerCallStack)();`), ['C', 'B']);
|
||||
assert.deepEqual(await page.evaluate(`(() => window._customEscHandlerCallStack)();`), ['C', 'B']);
|
||||
});
|
||||
});
|
||||
describe('addOscHandler', () => {
|
||||
it('should respects return value', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
const _customOscHandlerCallStack = [];
|
||||
window._customOscHandlerCallStack = [];
|
||||
const _customOscHandlerA = window.term.parser.addOscHandler(1234, data => {
|
||||
_customOscHandlerCallStack.push(['A', data]);
|
||||
window._customOscHandlerCallStack.push(['A', data]);
|
||||
return false;
|
||||
});
|
||||
const _customOscHandlerB = window.term.parser.addOscHandler(1234, data => {
|
||||
_customOscHandlerCallStack.push(['B', data]);
|
||||
window._customOscHandlerCallStack.push(['B', data]);
|
||||
return true;
|
||||
});
|
||||
const _customOscHandlerC = window.term.parser.addOscHandler(1234, data => {
|
||||
_customOscHandlerCallStack.push(['C', data]);
|
||||
window._customOscHandlerCallStack.push(['C', data]);
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await writeSync(page, '\x1b]1234;some data\x07');
|
||||
assert.deepEqual(await page.evaluate(`(() => _customOscHandlerCallStack)();`), [['C', 'some data'], ['B', 'some data']]);
|
||||
assert.deepEqual(await page.evaluate(`(() => window._customOscHandlerCallStack)();`), [['C', 'some data'], ['B', 'some data']]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user