From e5d60c0895565934eabeaefab832ad539d4743ad Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:00:16 -0700 Subject: [PATCH 01/24] Fix demo warnings by adding complex options --- demo/client.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/demo/client.ts b/demo/client.ts index 4c63e90f..a1e0f8b6 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -420,8 +420,11 @@ function initOptions(term: TerminalType): void { 'termName', 'cols', 'rows', // subsumed by "size" (cols_rows) option // Complex option + 'linkHandler', + 'logger', 'theme', - 'windowOptions' + 'windowOptions', + 'windowsPty' ]; const stringOptions = { cursorStyle: ['block', 'underline', 'bar'], From ee0da5e4c197cd28937420790284779633bc2e3a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:03:25 -0700 Subject: [PATCH 02/24] Fix demo client.ts warnings --- demo/client.ts | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/demo/client.ts b/demo/client.ts index 4c63e90f..d0547fb1 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -59,7 +59,7 @@ let socket; let pid; let autoResize: boolean = true; -type AddonType = 'attach' | 'canvas' | 'fit' | 'image' | 'search' | 'serialize' | 'unicode11' | 'web-links' | 'webgl' | 'ligatures'; +type AddonType = 'attach' | 'canvas' | 'fit' | 'image' | 'search' | 'serialize' | 'unicode11' | 'webLinks' | 'webgl' | 'ligatures'; interface IDemoAddon { name: T; @@ -71,7 +71,7 @@ interface IDemoAddon { T extends 'image' ? typeof ImageAddon : T extends 'search' ? typeof SearchAddon : T extends 'serialize' ? typeof SerializeAddon : - T extends 'web-links' ? typeof WebLinksAddon : + T extends 'webLinks' ? typeof WebLinksAddon : T extends 'unicode11' ? typeof Unicode11Addon : T extends 'ligatures' ? typeof LigaturesAddon : typeof WebglAddon @@ -83,7 +83,7 @@ interface IDemoAddon { T extends 'image' ? ImageAddon : T extends 'search' ? SearchAddon : T extends 'serialize' ? SerializeAddon : - T extends 'web-links' ? WebLinksAddon : + T extends 'webLinks' ? WebLinksAddon : T extends 'webgl' ? WebglAddon : T extends 'unicode11' ? typeof Unicode11Addon : T extends 'ligatures' ? typeof LigaturesAddon : @@ -98,7 +98,7 @@ const addons: { [T in AddonType]: IDemoAddon } = { image: { name: 'image', ctor: ImageAddon, canChange: true }, search: { name: 'search', ctor: SearchAddon, canChange: true }, serialize: { name: 'serialize', ctor: SerializeAddon, canChange: true }, - 'web-links': { name: 'web-links', ctor: WebLinksAddon, canChange: true }, + webLinks: { name: 'webLinks', ctor: WebLinksAddon, canChange: true }, webgl: { name: 'webgl', ctor: WebglAddon, canChange: true }, unicode11: { name: 'unicode11', ctor: Unicode11Addon, canChange: true }, ligatures: { name: 'ligatures', ctor: LigaturesAddon, canChange: true } @@ -170,7 +170,7 @@ const disposeRecreateButtonHandler: () => void = () => { addons.serialize.instance = undefined; addons.unicode11.instance = undefined; addons.ligatures.instance = undefined; - addons['web-links'].instance = undefined; + addons.webLinks.instance = undefined; addons.webgl.instance = undefined; document.getElementById('dispose').innerHTML = 'Recreate Terminal'; } else { @@ -272,13 +272,13 @@ function createTerminal(): void { } catch (e) { console.warn(e); } - addons['web-links'].instance = new WebLinksAddon(); + addons.webLinks.instance = new WebLinksAddon(); typedTerm.loadAddon(addons.fit.instance); typedTerm.loadAddon(addons.image.instance); typedTerm.loadAddon(addons.search.instance); typedTerm.loadAddon(addons.serialize.instance); typedTerm.loadAddon(addons.unicode11.instance); - typedTerm.loadAddon(addons['web-links'].instance); + typedTerm.loadAddon(addons.webLinks.instance); window.term = term; // Expose `term` to window for debugging purposes term.onResize((size: { cols: number, rows: number }) => { @@ -418,7 +418,7 @@ function initOptions(term: TerminalType): void { 'cancelEvents', 'convertEol', 'termName', - 'cols', 'rows', // subsumed by "size" (cols_rows) option + 'cols', 'rows', // subsumed by "size" (colsRows) option // Complex option 'theme', 'windowOptions' @@ -433,7 +433,7 @@ function initOptions(term: TerminalType): void { logLevel: ['trace', 'debug', 'info', 'warn', 'error', 'off'], theme: ['default', 'xtermjs', 'sapphire', 'light'], wordSeparator: null, - cols_rows: null + colsRows: null }; const options = Object.getOwnPropertyNames(term.options); const booleanOptions = []; @@ -466,7 +466,7 @@ function initOptions(term: TerminalType): void { }); html += '
'; Object.keys(stringOptions).forEach(o => { - if (o === 'cols_rows') { + if (o === 'colsRows') { html += `
`; } else if (stringOptions[o]) { const selectedOption = o === 'theme' ? 'xtermjs' : term.options[o]; @@ -511,8 +511,8 @@ function initOptions(term: TerminalType): void { addDomListener(input, 'change', () => { console.log('change', o, input.value); let value: any = input.value; - if (o === 'cols_rows') { - let m = input.value.match(/^([0-9]+)x([0-9]+)$/); + if (o === 'colsRows') { + const m = input.value.match(/^([0-9]+)x([0-9]+)$/); if (m) { autoResize = false; term.resize(parseInt(m[1]), parseInt(m[2])); @@ -1275,29 +1275,29 @@ function initImageAddonExposed(): void { const ctorOptionsElement = document.querySelector('#image-options'); ctorOptionsElement.value = JSON.stringify(DEFAULT_OPTIONS, null, 2); - const sixel_demo = (url: string) => () => fetch(url) + const sixelDemo = (url: string) => () => fetch(url) .then(resp => resp.arrayBuffer()) .then(buffer => { term.write('\r\n'); term.write(new Uint8Array(buffer)); }); - const iip_demo = (url: string) => () => fetch(url) - .then(resp => resp.arrayBuffer()) - .then(buffer => { - const data = new Uint8Array(buffer); - let sdata = ''; - for (let i = 0; i < data.length; ++i) sdata += String.fromCharCode(data[i]); - term.write('\r\n'); - term.write(`\x1b]1337;File=inline=1;size=${data.length}:${btoa(sdata)}\x1b\\`); - }); + const iipDemo = (url: string) => () => fetch(url) + .then(resp => resp.arrayBuffer()) + .then(buffer => { + const data = new Uint8Array(buffer); + let sdata = ''; + for (let i = 0; i < data.length; ++i) sdata += String.fromCharCode(data[i]); + term.write('\r\n'); + term.write(`\x1b]1337;File=inline=1;size=${data.length}:${btoa(sdata)}\x1b\\`); + }); document.getElementById('image-demo1').addEventListener('click', - sixel_demo('https://raw.githubusercontent.com/saitoha/libsixel/master/images/snake.six')); + sixelDemo('https://raw.githubusercontent.com/saitoha/libsixel/master/images/snake.six')); document.getElementById('image-demo2').addEventListener('click', - sixel_demo('https://raw.githubusercontent.com/jerch/node-sixel/master/testfiles/test2.sixel')); + sixelDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/testfiles/test2.sixel')); document.getElementById('image-demo3').addEventListener('click', - iip_demo('https://raw.githubusercontent.com/jerch/node-sixel/master/palette.png')); + iipDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/palette.png')); // demo for image retrieval API term.element.addEventListener('click', (ev: MouseEvent) => { From 4534b56c1f7b7dc4a593bdc752df48c5a3072bd2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:20:55 -0700 Subject: [PATCH 03/24] Use ts-check in server and start --- demo/server.js | 63 ++++++++++----- demo/start.js | 13 +-- package.json | 2 + yarn.lock | 215 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 266 insertions(+), 27 deletions(-) diff --git a/demo/server.js b/demo/server.js index f477ae79..d399eac8 100644 --- a/demo/server.js +++ b/demo/server.js @@ -4,12 +4,14 @@ * demo to the public as is would introduce security risks for the host. **/ -var express = require('express'); -var expressWs = require('express-ws'); -var os = require('os'); -var pty = require('node-pty'); +// @ts-check -// Whether to use binary transport. +const express = require('express'); +const expressWs = require('express-ws'); +const os = require('os'); +const pty = require('node-pty'); + +/** Whether to use binary transport. */ const USE_BINARY = os.platform() !== "win32"; function startServer() { @@ -41,18 +43,30 @@ function startServer() { app.use('/src', express.static(__dirname + '/src')); app.post('/terminals', (req, res) => { - const env = Object.assign({}, process.env); + /** @type {{ [key: string]: string }} */ + const env = {}; + for (const k of Object.keys(process.env)) { + const v = process.env[k]; + if (v) { + env[k] = v; + } + } + // 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' ? 'pwsh.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' - }); + if (typeof req.query.cols !== 'string' || typeof req.query.rows !== 'string') { + console.error({ req }); + throw new Error('Unexpected query args'); + } + const cols = parseInt(req.query.cols); + const rows = parseInt(req.query.rows); + const term = pty.spawn(process.platform === 'win32' ? 'pwsh.exe' : 'bash', [], { + name: 'xterm-256color', + cols: cols ?? 80, + rows: rows ?? 24, + cwd: process.platform === 'win32' ? undefined : env.PWD, + env, + encoding: USE_BINARY ? null : 'utf8' + }); console.log('Created terminal with PID: ' + term.pid); terminals[term.pid] = term; @@ -65,16 +79,21 @@ function startServer() { }); app.post('/terminals/:pid/size', (req, res) => { - var pid = parseInt(req.params.pid), - cols = parseInt(req.query.cols), - rows = parseInt(req.query.rows), - term = terminals[pid]; + if (typeof req.query.cols !== 'string' || typeof req.query.rows !== 'string') { + console.error({ req }); + throw new Error('Unexpected query args'); + } + const pid = parseInt(req.params.pid); + const cols = parseInt(req.query.cols); + const rows = parseInt(req.query.rows); + const term = terminals[pid]; term.resize(cols, rows); console.log('Resized terminal ' + pid + ' to ' + cols + ' cols and ' + rows + ' rows.'); res.end(); }); + // @ts-ignore app.ws('/terminals/:pid', function (ws, req) { var term = terminals[parseInt(req.params.pid)]; console.log('Connected to terminal ' + term.pid); @@ -160,11 +179,11 @@ function startServer() { }); }); - var port = process.env.PORT || 3000, + var port = parseInt(process.env.PORT ?? '3000'), host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0'; console.log('App listening to http://127.0.0.1:' + port); - app.listen(port, host); + app.listen(port, host, 0); } module.exports = startServer; diff --git a/demo/start.js b/demo/start.js index b40b9bc3..7349f36d 100644 --- a/demo/start.js +++ b/demo/start.js @@ -1,10 +1,10 @@ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT - * - * This file is the entry point for browserify. */ +// @ts-check + const path = require('path'); const webpack = require('webpack'); const startServer = require('./server.js'); @@ -20,6 +20,8 @@ startServer(); * For production builds see `webpack.config.js` in the root directory. If that is built the demo * can use that by switching out which `Terminal` is imported in `client.ts`, this is useful for * validating that the packaged version works correctly. + * + * @type {import('webpack').Configuration} */ const clientConfig = { entry: path.resolve(__dirname, 'client.ts'), @@ -69,12 +71,13 @@ const clientConfig = { const compiler = webpack(clientConfig); compiler.watch({ - // Example watchOptions aggregateTimeout: 300, poll: undefined }, (err, stats) => { - // Print watch/build result here... - console.log(stats.toString({ + if (err) { + console.error(err); + } + console.log(stats?.toString({ colors: true })); }); diff --git a/package.json b/package.json index be507b6e..59c53f91 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "@types/chai": "^4.2.22", "@types/debug": "^4.1.7", "@types/deep-equal": "^1.0.1", + "@types/express": "4", "@types/glob": "^7.2.0", "@types/jsdom": "^16.2.13", "@types/mocha": "^9.0.0", @@ -70,6 +71,7 @@ "chai": "^4.3.4", "cross-env": "^7.0.3", "deep-equal": "^2.0.5", + "esbuild": "^0.19.2", "eslint": "^8.45.0", "eslint-plugin-jsdoc": "^39.3.6", "express": "^4.17.1", diff --git a/yarn.lock b/yarn.lock index cde76d90..e989d88d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -214,6 +214,116 @@ esquery "^1.4.0" jsdoc-type-pratt-parser "~3.1.0" +"@esbuild/android-arm64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz#bc35990f412a749e948b792825eef7df0ce0e073" + integrity sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw== + +"@esbuild/android-arm@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.2.tgz#edd1c8f23ba353c197f5b0337123c58ff2a56999" + integrity sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q== + +"@esbuild/android-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.2.tgz#2dcdd6e6f1f2d82ea1b746abd8da5b284960f35a" + integrity sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w== + +"@esbuild/darwin-arm64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz#55b36bc06d76f5c243987c1f93a11a80d8fc3b26" + integrity sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA== + +"@esbuild/darwin-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz#982524af33a6424a3b5cb44bbd52559623ad719c" + integrity sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw== + +"@esbuild/freebsd-arm64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz#8e478a0856645265fe79eac4b31b52193011ee06" + integrity sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ== + +"@esbuild/freebsd-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz#01b96604f2540db023c73809bb8ae6cd1692d6f3" + integrity sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw== + +"@esbuild/linux-arm64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz#7e5d2c7864c5c83ec789b59c77cd9c20d2594916" + integrity sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg== + +"@esbuild/linux-arm@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz#c32ae97bc0246664a1cfbdb4a98e7b006d7db8ae" + integrity sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg== + +"@esbuild/linux-ia32@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz#3fc4f0fa026057fe885e4a180b3956e704f1ceaa" + integrity sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ== + +"@esbuild/linux-loong64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz#633bcaea443f3505fb0ed109ab840c99ad3451a4" + integrity sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw== + +"@esbuild/linux-mips64el@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz#e0bff2898c46f52be7d4dbbcca8b887890805823" + integrity sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg== + +"@esbuild/linux-ppc64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz#d75798da391f54a9674f8c143b9a52d1dbfbfdde" + integrity sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw== + +"@esbuild/linux-riscv64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz#012409bd489ed1bb9b775541d4a46c5ded8e6dd8" + integrity sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw== + +"@esbuild/linux-s390x@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz#ece3ed75c5a150de8a5c110f02e97d315761626b" + integrity sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g== + +"@esbuild/linux-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz#dea187019741602d57aaf189a80abba261fbd2aa" + integrity sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ== + +"@esbuild/netbsd-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz#bbfd7cf9ab236a23ee3a41b26f0628c57623d92a" + integrity sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ== + +"@esbuild/openbsd-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz#fa5c4c6ee52a360618f00053652e2902e1d7b4a7" + integrity sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw== + +"@esbuild/sunos-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz#52a2ac8ac6284c02d25df22bb4cfde26fbddd68d" + integrity sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw== + +"@esbuild/win32-arm64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz#719ed5870855de8537aef8149694a97d03486804" + integrity sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg== + +"@esbuild/win32-ia32@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz#24832223880b0f581962c8660f8fb8797a1e046a" + integrity sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA== + +"@esbuild/win32-x64@0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz#1205014625790c7ff0e471644a878a65d1e34ab0" + integrity sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw== + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -357,6 +467,14 @@ resolved "https://registry.yarnpkg.com/@types/app-root-path/-/app-root-path-1.2.5.tgz#71b6b3ad55061ad02e4a75e909b0c5fe776ae12c" integrity sha512-uJsNeY7Jwci2yDpjx0b99Vb7KOxAI7kgz7L7a19bXZMRFEhGSj0SZkGYg9nGgq+Zrp9nzEe+ceZRY68yIKqA5Q== +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + "@types/chai@^4.2.22": version "4.3.5" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.5.tgz#ae69bcbb1bebb68c4ac0b11e9d8ed04526b3562b" @@ -367,6 +485,13 @@ resolved "https://registry.yarnpkg.com/@types/cli-table/-/cli-table-0.3.1.tgz#a0ae06290284f7abebb90a2ddc0187de6d22e963" integrity sha512-m3+6WWfSSl6zqoXy8uQQifbgqV7Gt6fsyWnHLgUWVtJQk75+OfUB+edSZ52YDj7leSiZtX7w1/E4w2x/Hb0orA== +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + "@types/debug@^4.1.7": version "4.1.8" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.8.tgz#cef723a5d0a90990313faec2d1e22aee5eecb317" @@ -400,6 +525,26 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== +"@types/express-serve-static-core@^4.17.33": + version "4.17.36" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.36.tgz#baa9022119bdc05a4adfe740ffc97b5f9360e545" + integrity sha512-zbivROJ0ZqLAtMzgzIUC4oNqDG9iF0lSsAqpOD9kbs5xcIM3dTiyuHvBc7R8MtWBp3AAWGaovJa+wzWPjLYW7Q== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@4": + version "4.17.17" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" + integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + "@types/glob@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" @@ -408,6 +553,11 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/http-errors@*": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.1.tgz#20172f9578b225f6c7da63446f56d4ce108d5a65" + integrity sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ== + "@types/jsdom@^16.2.13": version "16.2.15" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.15.tgz#6c09990ec43b054e49636cba4d11d54367fc90d6" @@ -429,6 +579,16 @@ dependencies: decimal.js "^10.0.0" +"@types/mime@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + "@types/minimatch@*": version "5.1.2" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" @@ -476,11 +636,38 @@ dependencies: "@types/node" "*" +"@types/qs@*": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + "@types/semver@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== +"@types/send@*": + version "0.17.1" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.1.tgz#ed4932b8a2a805f1fe362a70f4e62d0ac994e301" + integrity sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.2.tgz#3e5419ecd1e40e7405d34093f10befb43f63381a" + integrity sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw== + dependencies: + "@types/http-errors" "*" + "@types/mime" "*" + "@types/node" "*" + "@types/tough-cookie@*": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" @@ -1467,6 +1654,34 @@ es6-error@^4.0.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== +esbuild@^0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.2.tgz#b1541828a89dfb6f840d38538767c6130dca2aac" + integrity sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg== + optionalDependencies: + "@esbuild/android-arm" "0.19.2" + "@esbuild/android-arm64" "0.19.2" + "@esbuild/android-x64" "0.19.2" + "@esbuild/darwin-arm64" "0.19.2" + "@esbuild/darwin-x64" "0.19.2" + "@esbuild/freebsd-arm64" "0.19.2" + "@esbuild/freebsd-x64" "0.19.2" + "@esbuild/linux-arm" "0.19.2" + "@esbuild/linux-arm64" "0.19.2" + "@esbuild/linux-ia32" "0.19.2" + "@esbuild/linux-loong64" "0.19.2" + "@esbuild/linux-mips64el" "0.19.2" + "@esbuild/linux-ppc64" "0.19.2" + "@esbuild/linux-riscv64" "0.19.2" + "@esbuild/linux-s390x" "0.19.2" + "@esbuild/linux-x64" "0.19.2" + "@esbuild/netbsd-x64" "0.19.2" + "@esbuild/openbsd-x64" "0.19.2" + "@esbuild/sunos-x64" "0.19.2" + "@esbuild/win32-arm64" "0.19.2" + "@esbuild/win32-ia32" "0.19.2" + "@esbuild/win32-x64" "0.19.2" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" From 25ef47a78ff2a6e5a10af20646c0d9748564280c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:25:15 -0700 Subject: [PATCH 04/24] Use express-ws types --- demo/server.js | 7 +++---- package.json | 1 + yarn.lock | 15 ++++++++++++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/demo/server.js b/demo/server.js index d399eac8..e6eb25f1 100644 --- a/demo/server.js +++ b/demo/server.js @@ -15,8 +15,8 @@ const pty = require('node-pty'); const USE_BINARY = os.platform() !== "win32"; function startServer() { - var app = express(); - expressWs(app); + const app = express(); + const appWs = expressWs(app).app; var terminals = {}, unsentOutput = {}, @@ -93,8 +93,7 @@ function startServer() { res.end(); }); - // @ts-ignore - app.ws('/terminals/:pid', function (ws, req) { + appWs.ws('/terminals/:pid', function (ws, req) { var term = terminals[parseInt(req.params.pid)]; console.log('Connected to terminal ' + term.pid); temporaryDisposable[term.pid].dispose(); diff --git a/package.json b/package.json index 59c53f91..9f702558 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "@types/debug": "^4.1.7", "@types/deep-equal": "^1.0.1", "@types/express": "4", + "@types/express-ws": "^3.0.1", "@types/glob": "^7.2.0", "@types/jsdom": "^16.2.13", "@types/mocha": "^9.0.0", diff --git a/yarn.lock b/yarn.lock index e989d88d..a1b8508e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -525,7 +525,7 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== -"@types/express-serve-static-core@^4.17.33": +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": version "4.17.36" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.36.tgz#baa9022119bdc05a4adfe740ffc97b5f9360e545" integrity sha512-zbivROJ0ZqLAtMzgzIUC4oNqDG9iF0lSsAqpOD9kbs5xcIM3dTiyuHvBc7R8MtWBp3AAWGaovJa+wzWPjLYW7Q== @@ -535,7 +535,16 @@ "@types/range-parser" "*" "@types/send" "*" -"@types/express@4": +"@types/express-ws@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/express-ws/-/express-ws-3.0.1.tgz#6fbf5dfdbeedd16479ccbeecbca63c14be26612e" + integrity sha512-VguRXzcpPBF0IggIGpUoM65cZJDfMQxoc6dKoCz1yLzcwcXW7ft60yhq3ygKhyEhEIQFtLrWjyz4AJ1qjmzCFw== + dependencies: + "@types/express" "*" + "@types/express-serve-static-core" "*" + "@types/ws" "*" + +"@types/express@*", "@types/express@4": version "4.17.17" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== @@ -687,7 +696,7 @@ tapable "^2.2.0" webpack "^5" -"@types/ws@^8.2.0": +"@types/ws@*", "@types/ws@^8.2.0": version "8.5.5" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== From 81baad6acd168e533a564548bbf85afe63ee6c37 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:26:00 -0700 Subject: [PATCH 05/24] Prefer const --- demo/server.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/demo/server.js b/demo/server.js index e6eb25f1..90ed2842 100644 --- a/demo/server.js +++ b/demo/server.js @@ -18,9 +18,9 @@ function startServer() { const app = express(); const appWs = expressWs(app).app; - var terminals = {}, - unsentOutput = {}, - temporaryDisposable = {}; + const terminals = {}; + const unsentOutput = {}; + const temporaryDisposable = {}; app.use('/xterm.css', express.static(__dirname + '/../css/xterm.css')); app.get('/logo.png', (req, res) => { @@ -94,7 +94,7 @@ function startServer() { }); appWs.ws('/terminals/:pid', function (ws, req) { - var term = terminals[parseInt(req.params.pid)]; + const term = terminals[parseInt(req.params.pid)]; console.log('Connected to terminal ' + term.pid); temporaryDisposable[term.pid].dispose(); delete temporaryDisposable[term.pid]; @@ -178,8 +178,8 @@ function startServer() { }); }); - var port = parseInt(process.env.PORT ?? '3000'), - host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0'; + const port = parseInt(process.env.PORT ?? '3000'); + const host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0'; console.log('App listening to http://127.0.0.1:' + port); app.listen(port, host, 0); From 9be0b6d918d524b9676a80e7f65efc5417f6fb96 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:29:38 -0700 Subject: [PATCH 06/24] Force eslintrc to use jsonc language --- .vscode/settings.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 07d643c6..af3cc208 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,7 @@ { + "files.associations": { + ".eslintrc.json.typings": "jsonc" + }, "typescript.preferences.importModuleSpecifier": "non-relative", "typescript.preferences.quoteStyle": "single", "mochaExplorer.envPath": ".mocha.env", From e78f85bf656c8e1db9533c9df3fba6958bc9f5cf Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:29:45 -0700 Subject: [PATCH 07/24] ts-check webpack configs --- webpack.config.headless.js | 5 ++++- webpack.config.js | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/webpack.config.headless.js b/webpack.config.headless.js index d5bb97b7..9e9099cd 100644 --- a/webpack.config.headless.js +++ b/webpack.config.headless.js @@ -10,8 +10,10 @@ const path = require('path'); * from tsc (via `yarn watch` or `yarn prebuild`) which are put into `out/` and webpacks them into a * production mode umd library module in `lib-headless/`. The aliases are used fix up the absolute * paths output by tsc (because of `baseUrl` and `paths` in `tsconfig.json`. + * + * @type {import('webpack').Configuration} */ -module.exports = { +const config = { entry: './out/headless/public/Terminal.js', devtool: 'source-map', module: { @@ -41,3 +43,4 @@ module.exports = { }, mode: 'production' }; +module.exports = config; diff --git a/webpack.config.js b/webpack.config.js index 4f9087b5..ca7c059a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,6 +3,8 @@ * @license MIT */ +// @ts-check + const path = require('path'); /** @@ -10,8 +12,10 @@ const path = require('path'); * (via `yarn watch` or `yarn prebuild`) which are put into `out/` and webpacks them into a * production mode umd library module in `lib/`. The aliases are used fix up the absolute paths * output by tsc (because of `baseUrl` and `paths` in `tsconfig.json`. + * + * @type {import('webpack').Configuration} */ -module.exports = { +const config = { entry: './out/browser/public/Terminal.js', devtool: 'source-map', module: { @@ -39,3 +43,4 @@ module.exports = { }, mode: 'production' }; +module.exports = config; From 6b794285c6fe8b9160744ec7bf6cc2dfc3451ff2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:32:22 -0700 Subject: [PATCH 08/24] Remove esbuild Accidentally added --- package.json | 1 - yarn.lock | 138 --------------------------------------------------- 2 files changed, 139 deletions(-) diff --git a/package.json b/package.json index 9f702558..de823832 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ "chai": "^4.3.4", "cross-env": "^7.0.3", "deep-equal": "^2.0.5", - "esbuild": "^0.19.2", "eslint": "^8.45.0", "eslint-plugin-jsdoc": "^39.3.6", "express": "^4.17.1", diff --git a/yarn.lock b/yarn.lock index a1b8508e..4b3a34ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -214,116 +214,6 @@ esquery "^1.4.0" jsdoc-type-pratt-parser "~3.1.0" -"@esbuild/android-arm64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz#bc35990f412a749e948b792825eef7df0ce0e073" - integrity sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw== - -"@esbuild/android-arm@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.2.tgz#edd1c8f23ba353c197f5b0337123c58ff2a56999" - integrity sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q== - -"@esbuild/android-x64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.2.tgz#2dcdd6e6f1f2d82ea1b746abd8da5b284960f35a" - integrity sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w== - -"@esbuild/darwin-arm64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz#55b36bc06d76f5c243987c1f93a11a80d8fc3b26" - integrity sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA== - -"@esbuild/darwin-x64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz#982524af33a6424a3b5cb44bbd52559623ad719c" - integrity sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw== - -"@esbuild/freebsd-arm64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz#8e478a0856645265fe79eac4b31b52193011ee06" - integrity sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ== - -"@esbuild/freebsd-x64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz#01b96604f2540db023c73809bb8ae6cd1692d6f3" - integrity sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw== - -"@esbuild/linux-arm64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz#7e5d2c7864c5c83ec789b59c77cd9c20d2594916" - integrity sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg== - -"@esbuild/linux-arm@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz#c32ae97bc0246664a1cfbdb4a98e7b006d7db8ae" - integrity sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg== - -"@esbuild/linux-ia32@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz#3fc4f0fa026057fe885e4a180b3956e704f1ceaa" - integrity sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ== - -"@esbuild/linux-loong64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz#633bcaea443f3505fb0ed109ab840c99ad3451a4" - integrity sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw== - -"@esbuild/linux-mips64el@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz#e0bff2898c46f52be7d4dbbcca8b887890805823" - integrity sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg== - -"@esbuild/linux-ppc64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz#d75798da391f54a9674f8c143b9a52d1dbfbfdde" - integrity sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw== - -"@esbuild/linux-riscv64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz#012409bd489ed1bb9b775541d4a46c5ded8e6dd8" - integrity sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw== - -"@esbuild/linux-s390x@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz#ece3ed75c5a150de8a5c110f02e97d315761626b" - integrity sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g== - -"@esbuild/linux-x64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz#dea187019741602d57aaf189a80abba261fbd2aa" - integrity sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ== - -"@esbuild/netbsd-x64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz#bbfd7cf9ab236a23ee3a41b26f0628c57623d92a" - integrity sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ== - -"@esbuild/openbsd-x64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz#fa5c4c6ee52a360618f00053652e2902e1d7b4a7" - integrity sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw== - -"@esbuild/sunos-x64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz#52a2ac8ac6284c02d25df22bb4cfde26fbddd68d" - integrity sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw== - -"@esbuild/win32-arm64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz#719ed5870855de8537aef8149694a97d03486804" - integrity sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg== - -"@esbuild/win32-ia32@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz#24832223880b0f581962c8660f8fb8797a1e046a" - integrity sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA== - -"@esbuild/win32-x64@0.19.2": - version "0.19.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz#1205014625790c7ff0e471644a878a65d1e34ab0" - integrity sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw== - "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -1663,34 +1553,6 @@ es6-error@^4.0.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -esbuild@^0.19.2: - version "0.19.2" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.2.tgz#b1541828a89dfb6f840d38538767c6130dca2aac" - integrity sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg== - optionalDependencies: - "@esbuild/android-arm" "0.19.2" - "@esbuild/android-arm64" "0.19.2" - "@esbuild/android-x64" "0.19.2" - "@esbuild/darwin-arm64" "0.19.2" - "@esbuild/darwin-x64" "0.19.2" - "@esbuild/freebsd-arm64" "0.19.2" - "@esbuild/freebsd-x64" "0.19.2" - "@esbuild/linux-arm" "0.19.2" - "@esbuild/linux-arm64" "0.19.2" - "@esbuild/linux-ia32" "0.19.2" - "@esbuild/linux-loong64" "0.19.2" - "@esbuild/linux-mips64el" "0.19.2" - "@esbuild/linux-ppc64" "0.19.2" - "@esbuild/linux-riscv64" "0.19.2" - "@esbuild/linux-s390x" "0.19.2" - "@esbuild/linux-x64" "0.19.2" - "@esbuild/netbsd-x64" "0.19.2" - "@esbuild/openbsd-x64" "0.19.2" - "@esbuild/sunos-x64" "0.19.2" - "@esbuild/win32-arm64" "0.19.2" - "@esbuild/win32-ia32" "0.19.2" - "@esbuild/win32-x64" "0.19.2" - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" From 1965daff504a2ec1ff8c32827e76e80d11b728e2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 24 Aug 2023 06:24:25 -0700 Subject: [PATCH 09/24] Warn on eslint rule @typescript-eslint/no-useless-constructor --- .eslintrc.json | 1 + addons/xterm-addon-fit/src/FitAddon.ts | 2 -- addons/xterm-addon-serialize/src/SerializeAddon.ts | 2 -- src/browser/Linkifier2.test.ts | 4 ---- src/common/Lifecycle.ts | 3 --- src/common/public/AddonManager.ts | 3 --- 6 files changed, 1 insertion(+), 14 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 7e8be09c..d206927e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -127,6 +127,7 @@ { "selector": "typeLike", "format": ["PascalCase"] }, { "selector": "interface", "format": ["PascalCase"], "prefix": ["I"] } ], + "@typescript-eslint/no-useless-constructor": "warn", "@typescript-eslint/prefer-namespace-keyword": "warn", "@typescript-eslint/type-annotation-spacing": "warn", "@typescript-eslint/quotes": [ diff --git a/addons/xterm-addon-fit/src/FitAddon.ts b/addons/xterm-addon-fit/src/FitAddon.ts index 6b3df6f0..f7245462 100644 --- a/addons/xterm-addon-fit/src/FitAddon.ts +++ b/addons/xterm-addon-fit/src/FitAddon.ts @@ -24,8 +24,6 @@ const MINIMUM_ROWS = 1; export class FitAddon implements ITerminalAddon { private _terminal: Terminal | undefined; - constructor() {} - public activate(terminal: Terminal): void { this._terminal = terminal; } diff --git a/addons/xterm-addon-serialize/src/SerializeAddon.ts b/addons/xterm-addon-serialize/src/SerializeAddon.ts index ace31315..7a2db089 100644 --- a/addons/xterm-addon-serialize/src/SerializeAddon.ts +++ b/addons/xterm-addon-serialize/src/SerializeAddon.ts @@ -414,8 +414,6 @@ class StringSerializeHandler extends BaseSerializeHandler { export class SerializeAddon implements ITerminalAddon { private _terminal: Terminal | undefined; - constructor() { } - public activate(terminal: Terminal): void { this._terminal = terminal; } diff --git a/src/browser/Linkifier2.test.ts b/src/browser/Linkifier2.test.ts index c1a0cf6c..0af74c28 100644 --- a/src/browser/Linkifier2.test.ts +++ b/src/browser/Linkifier2.test.ts @@ -10,10 +10,6 @@ import { MockBufferService } from 'common/TestUtils.test'; import { ILink } from 'browser/Types'; class TestLinkifier2 extends Linkifier2 { - constructor(bufferService: IBufferService) { - super(bufferService); - } - public set currentLink(link: any) { this._currentLink = link; } diff --git a/src/common/Lifecycle.ts b/src/common/Lifecycle.ts index 659403de..6e5ef27d 100644 --- a/src/common/Lifecycle.ts +++ b/src/common/Lifecycle.ts @@ -13,9 +13,6 @@ export abstract class Disposable implements IDisposable { protected _disposables: IDisposable[] = []; protected _isDisposed: boolean = false; - constructor() { - } - /** * Disposes the object, triggering the `dispose` method on all registered IDisposables. */ diff --git a/src/common/public/AddonManager.ts b/src/common/public/AddonManager.ts index 06c78121..af04a269 100644 --- a/src/common/public/AddonManager.ts +++ b/src/common/public/AddonManager.ts @@ -14,9 +14,6 @@ export interface ILoadedAddon { export class AddonManager implements IDisposable { protected _addons: ILoadedAddon[] = []; - constructor() { - } - public dispose(): void { for (let i = this._addons.length - 1; i >= 0; i--) { this._addons[i].instance.dispose(); From 2fe082beea9a10a3cad0e890253dd19d72f44e86 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 24 Aug 2023 06:33:11 -0700 Subject: [PATCH 10/24] Warn on eslint rule @typescript-eslint/no-confusing-void-expression --- .eslintrc.json | 4 ++++ addons/xterm-addon-fit/test/FitAddon.api.ts | 8 ++------ src/browser/public/Terminal.ts | 2 +- src/headless/public/Terminal.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 7e8be09c..822dd9af 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -127,6 +127,10 @@ { "selector": "typeLike", "format": ["PascalCase"] }, { "selector": "interface", "format": ["PascalCase"], "prefix": ["I"] } ], + "@typescript-eslint/no-confusing-void-expression": [ + "warn", + { "ignoreArrowShorthand": true } + ], "@typescript-eslint/prefer-namespace-keyword": "warn", "@typescript-eslint/type-annotation-spacing": "warn", "@typescript-eslint/quotes": [ diff --git a/addons/xterm-addon-fit/test/FitAddon.api.ts b/addons/xterm-addon-fit/test/FitAddon.api.ts index ef4618ea..36b44211 100644 --- a/addons/xterm-addon-fit/test/FitAddon.api.ts +++ b/addons/xterm-addon-fit/test/FitAddon.api.ts @@ -41,9 +41,7 @@ describe('FitAddon', () => { }); describe('proposeDimensions', () => { - afterEach(async () => { - return await unloadFit(); - }); + afterEach(() => unloadFit()); it('default', async function(): Promise { await loadFit(); @@ -82,9 +80,7 @@ describe('FitAddon', () => { }); describe('fit', () => { - afterEach(async () => { - return await unloadFit(); - }); + afterEach(() => unloadFit()); it('default', async function(): Promise { await loadFit(); diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 4db5f7aa..2c75d7b8 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -236,7 +236,7 @@ export class Terminal extends Disposable implements ITerminalApi { this._core.clearTextureAtlas(); } public loadAddon(addon: ITerminalAddon): void { - return this._addonManager.loadAddon(this, addon); + this._addonManager.loadAddon(this, addon); } public static get strings(): ILocalizableStrings { return Strings; diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index 5eeeb361..b018d37c 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -182,7 +182,7 @@ export class Terminal extends Disposable implements ITerminalApi { } public loadAddon(addon: ITerminalAddon): void { // TODO: This could cause issues if the addon calls renderer apis - return this._addonManager.loadAddon(this as any, addon); + this._addonManager.loadAddon(this as any, addon); } private _verifyIntegers(...values: number[]): void { From bd76cdbd20b5cf919a88d25764062a01f786aa30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 24 Aug 2023 17:24:25 +0200 Subject: [PATCH 11/24] resort wasm to xterm-wasm-parts --- .../out/base64.wasm.js/decode/definition.json | 1 - .../out/base64.wasm.js/decode/final.wasm | Bin 654 -> 0 bytes .../out/base64.wasm.js/decode/final.wat | 210 ---------- addons/xterm-addon-image/package.json | 7 +- addons/xterm-addon-image/src/IIPHandler.ts | 2 +- .../xterm-addon-image/src/base64.benchmark.ts | 80 ---- addons/xterm-addon-image/src/base64.test.ts | 156 -------- addons/xterm-addon-image/src/base64.wasm.ts | 372 ------------------ package.json | 2 - 9 files changed, 4 insertions(+), 826 deletions(-) delete mode 100644 addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/definition.json delete mode 100644 addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/final.wasm delete mode 100644 addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/final.wat delete mode 100644 addons/xterm-addon-image/src/base64.benchmark.ts delete mode 100644 addons/xterm-addon-image/src/base64.test.ts delete mode 100644 addons/xterm-addon-image/src/base64.wasm.ts diff --git a/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/definition.json b/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/definition.json deleted file mode 100644 index b4a652c8..00000000 --- a/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/definition.json +++ /dev/null @@ -1 +0,0 @@ -{"def":{"name":"decode","type":0,"mode":1,"srctype":"Clang-C","imports":{"env":{"memory":{}}},"exports":{},"compile":{"switches":["-Wl,-z,stack-size=0","-Wl,--stack-first"]},"code":"\n typedef struct {\n unsigned int wp;\n unsigned int sp;\n unsigned int dp;\n unsigned int e_size;\n unsigned int b_size;\n unsigned int dummy[3];\n unsigned char data[0];\n } State;\n\n unsigned int *D0 = (unsigned int *) 1024;\n unsigned int *D1 = (unsigned int *) 2048;\n unsigned int *D2 = (unsigned int *) 3072;\n unsigned int *D3 = (unsigned int *) 4096;\n State *state = (State *) 5120;\n\n __attribute__((noinline)) int dec() {\n unsigned int nsp = (state->wp - 1) & ~3;\n unsigned char *src = state->data + state->sp;\n unsigned char *end = state->data + nsp;\n unsigned char *dst = state->data + state->dp;\n unsigned int accu;\n\n while (src < end) {\n if ((accu = D0[src[0]] | D1[src[1]] | D2[src[2]] | D3[src[3]]) >> 24) return 1;\n *((unsigned int *) dst) = accu;\n dst += 3;\n src += 4;\n }\n state->sp = nsp;\n state->dp = dst - state->data;\n return 0;\n }\n\n int end() {\n int rem = state->wp - state->sp;\n if (rem > 4 && dec()) return 1;\n rem = state->wp - state->sp;\n if (rem < 2) return 1;\n\n unsigned char *src = state->data + state->sp;\n unsigned int accu = D0[src[0]] | D1[src[1]];\n int dp = 1;\n if (rem > 2 && src[2] != 61) {\n accu |= D2[src[2]];\n dp++;\n }\n if (rem == 4 && src[3] != 61) {\n accu |= D3[src[3]];\n dp++;\n }\n if (accu >> 24) return 1;\n *((unsigned int *) (state->data + state->dp)) = accu;\n state->dp += dp;\n return state->dp != state->b_size;\n }\n "},"memorySettings":{"descriptor":{"initial":1,"shared":false},"mode":"imported"},"srcDef":"{\n name: 'decode',\n type: 0 /* OutputType.INSTANCE */,\n mode: 1 /* OutputMode.SYNC */,\n srctype: 'Clang-C',\n imports: {\n env: { memory: new WebAssembly.Memory({ initial: 1 }) }\n },\n exports: {\n dec: () => 0,\n end: () => 0\n },\n compile: {\n switches: ['-Wl,-z,stack-size=0', '-Wl,--stack-first']\n },\n code: `\n typedef struct {\n unsigned int wp;\n unsigned int sp;\n unsigned int dp;\n unsigned int e_size;\n unsigned int b_size;\n unsigned int dummy[3];\n unsigned char data[0];\n } State;\n\n unsigned int *D0 = (unsigned int *) ${256 /* P32.D0 */ * 4};\n unsigned int *D1 = (unsigned int *) ${512 /* P32.D1 */ * 4};\n unsigned int *D2 = (unsigned int *) ${768 /* P32.D2 */ * 4};\n unsigned int *D3 = (unsigned int *) ${1024 /* P32.D3 */ * 4};\n State *state = (State *) ${1280 /* P32.STATE */ * 4};\n\n __attribute__((noinline)) int dec() {\n unsigned int nsp = (state->wp - 1) & ~3;\n unsigned char *src = state->data + state->sp;\n unsigned char *end = state->data + nsp;\n unsigned char *dst = state->data + state->dp;\n unsigned int accu;\n\n while (src < end) {\n if ((accu = D0[src[0]] | D1[src[1]] | D2[src[2]] | D3[src[3]]) >> 24) return 1;\n *((unsigned int *) dst) = accu;\n dst += 3;\n src += 4;\n }\n state->sp = nsp;\n state->dp = dst - state->data;\n return 0;\n }\n\n int end() {\n int rem = state->wp - state->sp;\n if (rem > 4 && dec()) return 1;\n rem = state->wp - state->sp;\n if (rem < 2) return 1;\n\n unsigned char *src = state->data + state->sp;\n unsigned int accu = D0[src[0]] | D1[src[1]];\n int dp = 1;\n if (rem > 2 && src[2] != 61) {\n accu |= D2[src[2]];\n dp++;\n }\n if (rem == 4 && src[3] != 61) {\n accu |= D3[src[3]];\n dp++;\n }\n if (accu >> 24) return 1;\n *((unsigned int *) (state->data + state->dp)) = accu;\n state->dp += dp;\n return state->dp != state->b_size;\n }\n `\n}","hash":""} \ No newline at end of file diff --git a/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/final.wasm b/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/final.wasm deleted file mode 100644 index c1ac6e8ad7ef12e6efe50ac5a5979513c0932ee6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 654 zcmZQbEY4+QU|?WmWlUgTtY_k9WKPX1W6Mp=%`d8CVqj!uW@2Dq=Vf9}Nlj*8U{1|T zVPNFi$jr2kk&(6Du|q?HiNSG!MwTL@V+)9_#NgNfq8%Bt9cv1em^@e<6qvvYm=z!j z8JHau7<3t!9hpirm>Lv7Y$ga>0>oy7umwPD1_+y@sHjMZ#qt0D|Loo@4vvic+zN~e zEM`m$3XG1-S&EDb42~>WN(>6jp1cg)+>R|83QT5942~Td3XF~mG_pZ#2JU~1jBNEx z4iMKV!Cjl}$m$0&Q;}JKftL{?3-gO21GgiiBC`Uc0zoMdKVVPT$<*>XppGMpu=C1Sd^Yx5}%fuSW;S)TFj))pIcgz an3R*Mo1T-Ol$cYj%~710o~N5yQ33#*5{exF diff --git a/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/final.wat b/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/final.wat deleted file mode 100644 index d7832f45..00000000 --- a/addons/xterm-addon-image/inwasm-builds/out/base64.wasm.js/decode/final.wat +++ /dev/null @@ -1,210 +0,0 @@ -(module - (type (;0;) (func (result i32))) - (import "env" "memory" (memory (;0;) 1)) - (func (;0;) (type 0) (result i32) - (local i32 i32 i32 i32 i32) - i32.const 5128 - i32.load - i32.const 5152 - i32.add - local.set 1 - i32.const 5124 - i32.load - local.tee 0 - i32.const 5120 - i32.load - i32.const 1 - i32.sub - i32.const -4 - i32.and - local.tee 2 - i32.lt_s - if ;; label = @1 - local.get 2 - i32.const 5152 - i32.add - local.set 3 - local.get 0 - i32.const 5152 - i32.add - local.set 0 - loop ;; label = @2 - local.get 0 - i32.load8_u offset=3 - i32.const 2 - i32.shl - i32.load offset=4096 - local.get 0 - i32.load8_u offset=2 - i32.const 2 - i32.shl - i32.load offset=3072 - local.get 0 - i32.load8_u offset=1 - i32.const 2 - i32.shl - i32.load offset=2048 - local.get 0 - i32.load8_u - i32.const 2 - i32.shl - i32.load offset=1024 - i32.or - i32.or - i32.or - local.tee 4 - i32.const 16777215 - i32.gt_u - if ;; label = @3 - i32.const 1 - return - end - local.get 1 - local.get 4 - i32.store - local.get 1 - i32.const 3 - i32.add - local.set 1 - local.get 0 - i32.const 4 - i32.add - local.tee 0 - local.get 3 - i32.lt_u - br_if 0 (;@2;) - end - end - i32.const 5124 - local.get 2 - i32.store - i32.const 5128 - local.get 1 - i32.const 5152 - i32.sub - i32.store - i32.const 0) - (func (;1;) (type 0) (result i32) - (local i32 i32 i32 i32 i32 i32) - block ;; label = @1 - i32.const 5120 - i32.load - local.tee 1 - i32.const 5124 - i32.load - local.tee 0 - i32.sub - i32.const 5 - i32.ge_s - if ;; label = @2 - i32.const 1 - local.set 3 - call 0 - br_if 1 (;@1;) - i32.const 5120 - i32.load - local.set 1 - i32.const 5124 - i32.load - local.set 0 - end - i32.const 1 - local.set 3 - local.get 1 - local.get 0 - i32.sub - local.tee 4 - i32.const 2 - i32.lt_s - br_if 0 (;@1;) - local.get 0 - i32.const 5153 - i32.add - i32.load8_u - i32.const 2 - i32.shl - i32.load offset=2048 - local.get 0 - i32.const 5152 - i32.add - i32.load8_u - i32.const 2 - i32.shl - i32.load offset=1024 - i32.or - local.set 1 - block ;; label = @2 - local.get 4 - i32.const 2 - i32.eq - if ;; label = @3 - i32.const 1 - local.set 2 - br 1 (;@2;) - end - i32.const 1 - local.set 2 - local.get 0 - i32.load8_u offset=5154 - local.tee 5 - i32.const 61 - i32.ne - if ;; label = @3 - i32.const 2 - local.set 2 - local.get 5 - i32.const 2 - i32.shl - i32.load offset=3072 - local.get 1 - i32.or - local.set 1 - end - local.get 4 - i32.const 4 - i32.ne - br_if 0 (;@2;) - local.get 0 - i32.load8_u offset=5155 - local.tee 0 - i32.const 61 - i32.eq - br_if 0 (;@2;) - local.get 2 - i32.const 1 - i32.add - local.set 2 - local.get 0 - i32.const 2 - i32.shl - i32.load offset=4096 - local.get 1 - i32.or - local.set 1 - end - local.get 1 - i32.const 16777215 - i32.gt_u - br_if 0 (;@1;) - i32.const 5128 - i32.load - i32.const 5152 - i32.add - local.get 1 - i32.store - i32.const 5128 - i32.const 5128 - i32.load - local.get 2 - i32.add - local.tee 0 - i32.store - local.get 0 - i32.const 5136 - i32.load - i32.ne - local.set 3 - end - local.get 3) - (export "dec" (func 0)) - (export "end" (func 1))) diff --git a/addons/xterm-addon-image/package.json b/addons/xterm-addon-image/package.json index 69b87b38..8de858b5 100644 --- a/addons/xterm-addon-image/package.json +++ b/addons/xterm-addon-image/package.json @@ -17,8 +17,7 @@ "xterm.js" ], "scripts": { - "inwasm": "inwasm out/*.wasm.js", - "prepackage": "../../node_modules/.bin/tsc -p . && inwasm -f out/*.wasm.js", + "prepackage": "../../node_modules/.bin/tsc -p .", "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package" }, @@ -26,7 +25,7 @@ "xterm": "^5.2.0" }, "devDependencies": { - "inwasm": "^0.0.13", - "sixel": "^0.16.0" + "sixel": "^0.16.0", + "xterm-wasm-parts": "^0.1.0" } } diff --git a/addons/xterm-addon-image/src/IIPHandler.ts b/addons/xterm-addon-image/src/IIPHandler.ts index a8a851e6..ae62100d 100644 --- a/addons/xterm-addon-image/src/IIPHandler.ts +++ b/addons/xterm-addon-image/src/IIPHandler.ts @@ -5,7 +5,7 @@ import { IImageAddonOptions, IOscHandler, IResetHandler, ITerminalExt } from './Types'; import { ImageRenderer } from './ImageRenderer'; import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage'; -import { Base64Decoder } from './base64.wasm'; +import Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm'; import { HeaderParser, IHeaderFields, HeaderState } from './IIPHeaderParser'; import { imageType, UNSUPPORTED_TYPE } from './IIPMetrics'; diff --git a/addons/xterm-addon-image/src/base64.benchmark.ts b/addons/xterm-addon-image/src/base64.benchmark.ts deleted file mode 100644 index 6870295d..00000000 --- a/addons/xterm-addon-image/src/base64.benchmark.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { ThroughputRuntimeCase, perfContext } from 'xterm-benchmark'; -import { Base64Decoder } from './base64.wasm'; - -// eslint-disable-next-line -declare const Buffer: any; - -function toBytes(s: string): Uint8Array { - const bytes = new Uint8Array(s.length); - for (let i = 0; i < s.length; ++i) { - bytes[i] = s.charCodeAt(i) & 0xFF; - } - return bytes; -} - -const d256 = 'ABCD'.repeat(64); -const d4096 = 'ABCD'.repeat(64 * 16); -const d65536 = 'ABCD'.repeat(64 * 16 * 16); -const d1M = 'ABCD'.repeat(64 * 16 * 16 * 16); -const b256 = toBytes(d256); -const b4096 = toBytes(d4096); -const b65536 = toBytes(d65536); -const b1M = toBytes(d1M); -const dec = new Base64Decoder(4000000); - - -const RUNS = 100; - -perfContext('Base64', () => { - perfContext('Node - Buffer', () => { - new ThroughputRuntimeCase('decode - 256', () => { - Buffer.from(d256, 'base64'); - return { payloadSize: d256.length }; - }, { repeat: RUNS }).showAverageThroughput(); - - new ThroughputRuntimeCase('decode - 4096', () => { - Buffer.from(d4096, 'base64'); - return { payloadSize: d4096.length }; - }, { repeat: RUNS }).showAverageThroughput(); - - new ThroughputRuntimeCase('decode - 65536', () => { - Buffer.from(d65536, 'base64'); - return { payloadSize: d65536.length }; - }, { repeat: RUNS }).showAverageThroughput(); - - new ThroughputRuntimeCase('decode - 1048576', () => { - Buffer.from(d1M, 'base64'); - return { payloadSize: d1M.length }; - }, { repeat: RUNS }).showAverageThroughput(); - }); - - perfContext('Base64Decoder', () => { - new ThroughputRuntimeCase('decode - 256', () => { - dec.init(192); - dec.put(b256, 0, b256.length); - dec.end(); - return { payloadSize: b256.length }; - }, { repeat: RUNS }).showAverageThroughput(); - - new ThroughputRuntimeCase('decode - 4096', () => { - dec.init(3072); - dec.put(b4096, 0, b4096.length); - dec.end(); - return { payloadSize: b4096.length }; - }, { repeat: RUNS }).showAverageThroughput(); - - new ThroughputRuntimeCase('decode - 65536', () => { - dec.init(49152); - dec.put(b65536, 0, b65536.length); - dec.end(); - return { payloadSize: b65536.length }; - }, { repeat: RUNS }).showAverageThroughput(); - - new ThroughputRuntimeCase('decode - 1048576', () => { - dec.init(786432); - dec.put(b1M, 0, b1M.length); - dec.end(); - return { payloadSize: b1M.length }; - }, { repeat: RUNS }).showAverageThroughput(); - }); -}); diff --git a/addons/xterm-addon-image/src/base64.test.ts b/addons/xterm-addon-image/src/base64.test.ts deleted file mode 100644 index 2d4e6d76..00000000 --- a/addons/xterm-addon-image/src/base64.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { assert } from 'chai'; -import { Base64Decoder } from './base64.wasm'; - -// eslint-disable-next-line -declare const Buffer: any; - - -// some helpers -function toBs(bytes: Uint8Array): string { - let bs = ''; - for (let i = 0; i < bytes.length; ++i) bs += String.fromCharCode(bytes[i]); - return bs; -} -function fromBs(bs: string): Uint8Array { - const r = new Uint8Array(bs.length); - for (let i = 0; i < r.length; ++i) r[i] = bs.charCodeAt(i); - return r; -} -function encNative(bytes: Uint8Array): string { - return typeof Buffer !== 'undefined' ? Buffer.from(bytes).toString('base64') : btoa(toBs(bytes)); -} -function rtrim(x: string, c: string): string { - let end = x.length - 1; - while (c.indexOf(x[end]) >= 0) end -= 1; - return x.slice(0, end + 1); -} -const MAP = new Uint8Array( - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' - .split('') - .map(el => el.charCodeAt(0)) -); - - -describe('Base64Decoder', () => { - describe('decoding', () => { - it('single bytes', function() { - this.timeout(20000); - const dec = new Base64Decoder(0); - for (let i = 0; i < 256; ++i) { - dec.init(1); - const inp = new Uint8Array([i]); - const data = fromBs(encNative(inp)); - assert.strictEqual(dec.put(data, 0, data.length), 0); - assert.strictEqual(dec.end(), 0); - assert.deepEqual(dec.data8, inp); - } - }); - for (let a = 0; a < 256; ++a) { - it(`1+2 bytes (${a})`, function() { - const dec = new Base64Decoder(0); - for (let b = 0; b < 256; ++b) { - dec.init(2); - const inp = new Uint8Array([a, b]); - const data = fromBs(encNative(inp)); - assert.strictEqual(dec.put(data, 0, data.length), 0); - assert.strictEqual(dec.end(), 0); - assert.deepEqual(dec.data8, inp); - } - }); - } - for (let a = 0; a < 256; ++a) { - it(`2+3 bytes (${a})`, function() { - const dec = new Base64Decoder(0); - for (let b = 0; b < 256; ++b) { - dec.init(3); - const inp = new Uint8Array([0, a, b]); - const data = fromBs(encNative(inp)); - assert.strictEqual(dec.put(data, 0, data.length), 0); - assert.strictEqual(dec.end(), 0); - assert.deepEqual(dec.data8, inp); - } - }); - } - for (let a = 0; a < 256; ++a) { - it(`3+4 bytes (${a})`, function() { - const dec = new Base64Decoder(0); - for (let b = 0; b < 256; ++b) { - dec.init(4); - const inp = new Uint8Array([0, 0, a, b]); - const data = fromBs(encNative(inp)); - assert.strictEqual(dec.put(data, 0, data.length), 0); - assert.strictEqual(dec.end(), 0); - assert.deepEqual(dec.data8, inp); - } - }); - } - it('padding', () => { - const dec = new Base64Decoder(0); - const d = fromBs('Hello, here comes the mouse'); - const encData = []; - const encDataTrimmed = []; - for (let i = 1; i < d.length; ++i) { - encData.push(encNative(d.slice(0, i))); - encDataTrimmed.push(rtrim(encNative(d.slice(0, i)), '=')); - } - for (let i = 0; i < encData.length; ++i) { - // with padding - dec.init(i + 1); - let enc = fromBs(encData[i]); - assert.strictEqual(dec.put(enc, 0, enc.length), 0); - assert.strictEqual(dec.end(), 0); - assert.deepEqual(dec.data8, d.slice(0, i + 1)); - // w'o padding - dec.init(i + 1); - enc = fromBs(encDataTrimmed[i]); - assert.strictEqual(dec.put(enc, 0, enc.length), 0); - assert.strictEqual(dec.end(), 0); - assert.deepEqual(dec.data8, d.slice(0, i + 1)); - } - }); - it('exit on false byte', function() { - this.timeout(20000); - const dec = new Base64Decoder(0); - for (let pos = 0; pos < 8; ++pos) { - const inp = new Uint8Array([65, 65, 65, 65, 65, 65, 65, 65]); - for (let i = 0; i < 256; ++i) { - dec.release(); - dec.init(6); - inp[pos] = i; - dec.put(inp, 0, 8); - assert.strictEqual(dec.end(), MAP.includes(i) ? 0 : 1); - } - } - }); - }); - describe('memory', () => { - it('always release (keepSize 0)', () => { - const dec = new Base64Decoder(0); - dec.init(16); - dec.put(fromBs('A'.repeat(16)), 0, 16); - dec.end(); - assert.strictEqual(dec.data8.length, 12); - dec.release(); - assert.strictEqual(dec.data8.length, 0); - assert.isNull((dec as any)._mem); - }); - it('keep 1 page (keepSize 65536)', () => { - const dec = new Base64Decoder(65536); - dec.init(384); - dec.put(fromBs('A'.repeat(512)), 0, 512); - dec.end(); - assert.strictEqual(dec.data8.length, 384); - dec.release(); - assert.strictEqual(dec.data8.length, 0); - assert.isNotNull((dec as any)._mem); - // grow to 2 pages + free afterwards - dec.init(65536); - dec.put(fromBs('A'.repeat(65536)), 0, 65536); - dec.end(); - assert.strictEqual(dec.data8.length, 49152); - dec.release(); - assert.strictEqual(dec.data8.length, 0); - assert.isNull((dec as any)._mem); - }); - }); -}); diff --git a/addons/xterm-addon-image/src/base64.wasm.ts b/addons/xterm-addon-image/src/base64.wasm.ts deleted file mode 100644 index 02965c2d..00000000 --- a/addons/xterm-addon-image/src/base64.wasm.ts +++ /dev/null @@ -1,372 +0,0 @@ -/** - * Copyright (c) 2023 The xterm.js authors. All rights reserved. - * @license MIT - */ -import { InWasm, IWasmInstance, OutputMode, OutputType } from 'inwasm'; - - -// memory addresses in uint32 -const enum P32 { - D0 = 256, - D1 = 512, - D2 = 768, - D3 = 1024, - STATE = 1280, - STATE_WP = 1280, - STATE_SP = 1281, - STATE_DP = 1282, - STATE_ESIZE = 1283, - STATE_BSIZE = 1284, - STATE_DATA = 1288 // 16 aligned -} - -/** - * wasm base64 decoder. - */ -const wasmDecode = InWasm({ - name: 'decode', - type: OutputType.INSTANCE, - mode: OutputMode.SYNC, - srctype: 'Clang-C', - imports: { - env: { memory: new WebAssembly.Memory({ initial: 1 }) } - }, - exports: { - dec: () => 0, - end: () => 0 - }, - compile: { - switches: ['-Wl,-z,stack-size=0', '-Wl,--stack-first'] - }, - code: ` - typedef struct { - unsigned int wp; - unsigned int sp; - unsigned int dp; - unsigned int e_size; - unsigned int b_size; - unsigned int dummy[3]; - unsigned char data[0]; - } State; - - unsigned int *D0 = (unsigned int *) ${P32.D0*4}; - unsigned int *D1 = (unsigned int *) ${P32.D1*4}; - unsigned int *D2 = (unsigned int *) ${P32.D2*4}; - unsigned int *D3 = (unsigned int *) ${P32.D3*4}; - State *state = (State *) ${P32.STATE*4}; - - __attribute__((noinline)) int dec() { - unsigned int nsp = (state->wp - 1) & ~3; - unsigned char *src = state->data + state->sp; - unsigned char *end = state->data + nsp; - unsigned char *dst = state->data + state->dp; - unsigned int accu; - - while (src < end) { - if ((accu = D0[src[0]] | D1[src[1]] | D2[src[2]] | D3[src[3]]) >> 24) return 1; - *((unsigned int *) dst) = accu; - dst += 3; - src += 4; - } - state->sp = nsp; - state->dp = dst - state->data; - return 0; - } - - int end() { - int rem = state->wp - state->sp; - if (rem > 4 && dec()) return 1; - rem = state->wp - state->sp; - if (rem < 2) return 1; - - unsigned char *src = state->data + state->sp; - unsigned int accu = D0[src[0]] | D1[src[1]]; - int dp = 1; - if (rem > 2 && src[2] != 61) { - accu |= D2[src[2]]; - dp++; - } - if (rem == 4 && src[3] != 61) { - accu |= D3[src[3]]; - dp++; - } - if (accu >> 24) return 1; - *((unsigned int *) (state->data + state->dp)) = accu; - state->dp += dp; - return state->dp != state->b_size; - } - ` -}); - -// SIMD version - commented out for now due to missing Safari support -// const wasmDecode = InWasm({ -// name: 'decode', -// type: OutputType.INSTANCE, -// mode: OutputMode.SYNC, -// srctype: 'Clang-C', -// imports: { -// env: { memory: new WebAssembly.Memory({ initial: 1 }) } -// }, -// exports: { -// dec: () => 0, -// end: () => 0 -// }, -// compile: { -// switches: ['-msimd128', '-Wl,-z,stack-size=0', '-Wl,--stack-first'] -// }, -// code: ` -// #include -// typedef struct { -// unsigned int wp; -// unsigned int sp; -// unsigned int dp; -// unsigned int e_size; -// unsigned int b_size; -// unsigned int dummy[3]; -// unsigned char data[0]; -// } State; -// -// unsigned int *D0 = (unsigned int *) ${P32.D0*4}; -// unsigned int *D1 = (unsigned int *) ${P32.D1*4}; -// unsigned int *D2 = (unsigned int *) ${P32.D2*4}; -// unsigned int *D3 = (unsigned int *) ${P32.D3*4}; -// State *state = (State *) ${P32.STATE*4}; -// -// #define packed_byte(x) wasm_i8x16_splat((char) x) -// #define packed_dword(x) wasm_i32x4_splat(x) -// #define masked(x, mask) wasm_v128_and(x, wasm_i32x4_splat(mask)) -// -// int dec4() { -// unsigned int nsp = (state->wp - 1) & ~3; -// unsigned char *src = state->data + state->sp; -// unsigned char *end = state->data + nsp; -// unsigned char *dst = state->data + state->dp; -// unsigned int accu; -// -// while (src < end) { -// if ((accu = D0[src[0]] | D1[src[1]] | D2[src[2]] | D3[src[3]]) >> 24) return 1; -// *((unsigned int *) dst) = accu; -// dst += 3; -// src += 4; -// } -// state->sp = nsp; -// state->dp = dst - state->data; -// return 0; -// } -// -// int dec() { -// unsigned int nsp = (state->wp - 1) & ~15; -// unsigned char *src = state->data + state->sp; -// unsigned char *end = state->data + nsp; -// unsigned char *dst = state->data + state->dp; -// unsigned int accu; -// -// v128_t err = wasm_i8x16_splat(0); -// -// while (src < end) { -// v128_t data = wasm_v128_load((v128_t *) src); -// -// // wasm-simd rewrite of http://0x80.pl/notesen/2016-01-17-sse-base64-decoding.html#vector-lookup-pshufb -// const v128_t higher_nibble = wasm_u32x4_shr(data, 4) & packed_byte(0x0f); -// const char linv = 1; -// const char hinv = 0; -// -// const v128_t lower_bound_LUT = wasm_i8x16_make( -// /* 0 */ linv, /* 1 */ linv, /* 2 */ 0x2b, /* 3 */ 0x30, -// /* 4 */ 0x41, /* 5 */ 0x50, /* 6 */ 0x61, /* 7 */ 0x70, -// /* 8 */ linv, /* 9 */ linv, /* a */ linv, /* b */ linv, -// /* c */ linv, /* d */ linv, /* e */ linv, /* f */ linv -// ); -// const v128_t upper_bound_LUT = wasm_i8x16_make( -// /* 0 */ hinv, /* 1 */ hinv, /* 2 */ 0x2b, /* 3 */ 0x39, -// /* 4 */ 0x4f, /* 5 */ 0x5a, /* 6 */ 0x6f, /* 7 */ 0x7a, -// /* 8 */ hinv, /* 9 */ hinv, /* a */ hinv, /* b */ hinv, -// /* c */ hinv, /* d */ hinv, /* e */ hinv, /* f */ hinv -// ); -// // the difference between the shift and lower bound -// const v128_t shift_LUT = wasm_i8x16_make( -// /* 0 */ 0x00, /* 1 */ 0x00, /* 2 */ 0x3e - 0x2b, /* 3 */ 0x34 - 0x30, -// /* 4 */ 0x00 - 0x41, /* 5 */ 0x0f - 0x50, /* 6 */ 0x1a - 0x61, /* 7 */ 0x29 - 0x70, -// /* 8 */ 0x00, /* 9 */ 0x00, /* a */ 0x00, /* b */ 0x00, -// /* c */ 0x00, /* d */ 0x00, /* e */ 0x00, /* f */ 0x00 -// ); -// -// const v128_t upper_bound = wasm_i8x16_swizzle(upper_bound_LUT, higher_nibble); -// const v128_t lower_bound = wasm_i8x16_swizzle(lower_bound_LUT, higher_nibble); -// -// const v128_t below = wasm_i8x16_lt(data, lower_bound); -// const v128_t above = wasm_i8x16_gt(data, upper_bound); -// const v128_t eq_2f = wasm_i8x16_eq(data, packed_byte(0x2f)); -// -// // in_range = not (below or above) or eq_2f -// // outside = not in_range = below or above and not eq_2f (from deMorgan law) -// const v128_t outside = wasm_v128_andnot(eq_2f, above | below); -// err = wasm_v128_or(err, outside); -// -// const v128_t shift = wasm_i8x16_swizzle(shift_LUT, higher_nibble); -// const v128_t t0 = wasm_i8x16_add(data, shift); -// v128_t v = wasm_i8x16_add(t0, wasm_v128_and(eq_2f, packed_byte(-3))); -// -// // pack bytes -// const v128_t ca = masked(v, 0x003f003f); -// const v128_t db = masked(v, 0x3f003f00); -// const v128_t t00 = wasm_v128_or(wasm_u32x4_shr(db, 8), wasm_i32x4_shl(ca, 6)); -// v128_t res = wasm_v128_or(wasm_u32x4_shr(t00, 16), wasm_i32x4_shl(t00, 12)); -// res = wasm_i8x16_swizzle(res, wasm_i8x16_const(2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, 16, 16, 16, 16)); -// -// wasm_v128_store((v128_t *) dst, res); -// dst += 12; -// src += 16; -// } -// -// if (wasm_i8x16_bitmask(err) != 0) return 1; -// -// state->sp = nsp; -// state->dp = dst - state->data; -// return 0; -// } -// -// int end() { -// int rem = state->wp - state->sp; -// if (rem > 4 && dec4()) return 1; -// rem = state->wp - state->sp; -// if (rem < 2) return 1; -// -// unsigned char *src = state->data + state->sp; -// unsigned int accu = D0[src[0]] | D1[src[1]]; -// int dp = 1; -// if (rem > 2 && src[2] != 61) { -// accu |= D2[src[2]]; -// dp++; -// } -// if (rem == 4 && src[3] != 61) { -// accu |= D3[src[3]]; -// dp++; -// } -// if (accu >> 24) return 1; -// *((unsigned int *) (state->data + state->dp)) = accu; -// state->dp += dp; -// return state->dp != state->b_size; -// } -// ` -// }); - -// FIXME: currently broken in inwasm -type ExtractDefinition = Type extends () => IWasmInstance ? X : never; -type DecodeDefinition = ExtractDefinition; - -// base64 map -const MAP = new Uint8Array( - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' - .split('') - .map(el => el.charCodeAt(0)) -); - -// init decoder maps in LE order -const D = new Uint32Array(1024); -D.fill(0xFF000000); -for (let i = 0; i < MAP.length; ++i) D[MAP[i]] = i << 2; -for (let i = 0; i < MAP.length; ++i) D[256 + MAP[i]] = i >> 4 | ((i << 4) & 0xFF) << 8; -for (let i = 0; i < MAP.length; ++i) D[512 + MAP[i]] = (i >> 2) << 8 | ((i << 6) & 0xFF) << 16; -for (let i = 0; i < MAP.length; ++i) D[768 + MAP[i]] = i << 16; - -const EMPTY = new Uint8Array(0); - -/** - * base64 streamline inplace decoder. - * - * Features / assumptions: - * - optimized uint32 read/write (only LE support!) - * - lazy chunkwise decoding - * - errors out on any non base64 chars (no support for NL formatted base64) - * - decodes in wasm - * - inplace decoding to save memory - * - supports a keepSize for lazy memory release - */ -export class Base64Decoder { - private _d!: Uint8Array; - private _m32!: Uint32Array; - private _inst!: IWasmInstance; - private _mem!: WebAssembly.Memory; - - constructor(public keepSize: number) {} - - /** - * Currently decoded bytes (borrowed). - * Must be accessed before calling `release` or `init`. - */ - public get data8(): Uint8Array { - return this._inst ? this._d.subarray(0, this._m32[P32.STATE_DP]) : EMPTY; - } - - /** - * Release memory conditionally based on `keepSize`. - * If memory gets released, also the wasm instance will be freed and recreated on next `init`, - * otherwise the instance will be reused. - */ - public release(): void { - if (!this._inst) return; - if (this._mem.buffer.byteLength > this.keepSize) { - this._inst = this._m32 = this._d = this._mem = null!; - } else { - this._m32[P32.STATE_WP] = 0; - this._m32[P32.STATE_SP] = 0; - this._m32[P32.STATE_DP] = 0; - } - } - - /** - * Initializes the decoder for new base64 data. - * Must be called before doing any decoding attempts. - * `size` is the amount of decoded bytes to be expected. - * The method will either spawn a new wasm instance or grow - * the needed memory of an existing instance. - */ - public init(size: number): void { - let m = this._m32; - const bytes = (Math.ceil(size / 3) + P32.STATE_DATA) * 4; - if (!this._inst) { - this._mem = new WebAssembly.Memory({ initial: Math.ceil(bytes / 65536) }); - this._inst = wasmDecode({ env: { memory: this._mem } }); - m = new Uint32Array(this._mem.buffer, 0); - m.set(D, P32.D0); - this._d = new Uint8Array(this._mem.buffer, P32.STATE_DATA * 4); - } else if (this._mem.buffer.byteLength < bytes) { - this._mem.grow(Math.ceil((bytes - this._mem.buffer.byteLength) / 65536)); - m = new Uint32Array(this._mem.buffer, 0); - this._d = new Uint8Array(this._mem.buffer, P32.STATE_DATA * 4); - } - m[P32.STATE_BSIZE] = size; - m[P32.STATE_ESIZE] = Math.ceil(size / 3) * 4; - m[P32.STATE_WP] = 0; - m[P32.STATE_SP] = 0; - m[P32.STATE_DP] = 0; - this._m32 = m; - } - - /** - * Put bytes in `data` from `start` to `end` (exclusive) into the decoder. - * Also decodes base64 data inplace once the payload exceeds 2^17 bytes. - * Returns 1 on error, else 0. - */ - public put(data: Uint8Array | Uint16Array | Uint32Array, start: number, end: number): number { - if (!this._inst) return 1; - const m = this._m32; - if (end - start + m[P32.STATE_WP] > m[P32.STATE_ESIZE]) return 1; - this._d.set(data.subarray(start, end), m[P32.STATE_WP]); - m[P32.STATE_WP] += end - start; - // max chunk in input handler is 2^17, try to run in "tandem mode" - // also assures that we dont run into illegal offsets in the wasm part - return m[P32.STATE_WP] - m[P32.STATE_SP] >= 131072 ? this._inst.exports.dec() : 0; - } - - /** - * End the current decoding. - * Decodes leftover payload and finally checks for the correct amount of - * decoded bytes by comparing to the value given to `init`. - * Returns 1 on error, else 0. - */ - public end(): number { - return this._inst ? this._inst.exports.end() : 1; - } -} diff --git a/package.json b/package.json index de823832..70214933 100644 --- a/package.json +++ b/package.json @@ -44,10 +44,8 @@ "install-addons": "node ./bin/install-addons.js", "presetup": "npm run install-addons", "setup": "npm run build", - "postsetup": "npm run inwasm", "prepublishOnly": "npm run package", "watch": "tsc -b -w ./tsconfig.all.json --preserveWatchOutput", - "inwasm": "cd addons/xterm-addon-image && npm run inwasm -- -S", "benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json", "benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/test/benchmark/*benchmark.js", "benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/test/benchmark/*benchmark.js", From 7c164efdcc7833b038875511345f9edaa279c9a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Thu, 24 Aug 2023 17:30:52 +0200 Subject: [PATCH 12/24] fix CI scripts --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd465c01..75337542 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,6 @@ jobs: ./addons/xterm-addon-canvas/out-test/* \ ./addons/xterm-addon-fit/out/* \ ./addons/xterm-addon-fit/out-test/* \ - ./addons/xterm-addon-image/inwasm-builds/out/* \ ./addons/xterm-addon-image/out/* \ ./addons/xterm-addon-image/out-test/* \ ./addons/xterm-addon-ligatures/out/* \ From 797a71695fd1d2d5d74dae9972221a4405071047 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 26 Aug 2023 09:37:08 -0700 Subject: [PATCH 13/24] Improve texture atlas utilization - Changed the threshold at which pages start to merge from MAX/2 to MAX. - If a glyph is about to create a new page that will be merged, check if the existing row fits when ignoring the row pixel threshold. This will improve texture utilization by using the available space before the page is merged and becomes static. --- src/browser/renderer/shared/TextureAtlas.ts | 34 +++++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/browser/renderer/shared/TextureAtlas.ts b/src/browser/renderer/shared/TextureAtlas.ts index c7b85a7e..4444d8f6 100644 --- a/src/browser/renderer/shared/TextureAtlas.ts +++ b/src/browser/renderer/shared/TextureAtlas.ts @@ -151,7 +151,7 @@ export class TextureAtlas implements ITextureAtlas { // microtask to ensure it does not interrupt textures that will be rendered in the current // animation frame which would result in blank rendered areas. This is actually not that // expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. - if (TextureAtlas.maxAtlasPages && this._pages.length >= Math.max(4, TextureAtlas.maxAtlasPages / 2)) { + if (TextureAtlas.maxAtlasPages && this._pages.length >= Math.max(4, TextureAtlas.maxAtlasPages)) { queueMicrotask(() => { // Find the set of the largest 4 images, below the maximum size, with the highest // percentages used @@ -756,13 +756,13 @@ export class TextureAtlas implements ITextureAtlas { } } - // Create a new one if too much vertical space would be wasted or there is not enough room + // Create a new page if too much vertical space would be wasted or there is not enough room // left in the page. The previous active row will become fixed in the process as it now has a // fixed height if (activeRow.y + rasterizedGlyph.size.y >= activePage.canvas.height || activeRow.height > rasterizedGlyph.size.y + Constants.ROW_PIXEL_THRESHOLD) { // Create the new fixed height row, creating a new page if there isn't enough room on the // current page - let wasNewPageCreated = false; + let wasPageAndRowFound = false; if (activePage.currentRow.y + activePage.currentRow.height + rasterizedGlyph.size.y >= activePage.canvas.height) { // Find the first page with room to create the new row on let candidatePage: AtlasPage | undefined; @@ -775,15 +775,29 @@ export class TextureAtlas implements ITextureAtlas { if (candidatePage) { activePage = candidatePage; } else { - // Create a new page if there is no room - const newPage = this._createNewPage(); - activePage = newPage; - activeRow = newPage.currentRow; - activeRow.height = rasterizedGlyph.size.y; - wasNewPageCreated = true; + // Before creating a new atlas page that would trigger a page merge, check if the + // current active row is sufficient when ignoring the ROW_PIXEL_THRESHOLD. This will + // improve texture utilization by using the available space before the page is merged + // and becomes static. + if ( + TextureAtlas.maxAtlasPages === this._pages.length && + activeRow.y + rasterizedGlyph.size.y <= activePage.canvas.height && + activeRow.height >= rasterizedGlyph.size.y && + activeRow.x + rasterizedGlyph.size.x <= activePage.canvas.width + ) { + // activePage and activeRow is already valid + wasPageAndRowFound = true; + } else { + // Create a new page if there is no room + const newPage = this._createNewPage(); + activePage = newPage; + activeRow = newPage.currentRow; + activeRow.height = rasterizedGlyph.size.y; + wasPageAndRowFound = true; + } } } - if (!wasNewPageCreated) { + if (!wasPageAndRowFound) { // Fix the current row as the new row is being added below if (activePage.currentRow.height > 0) { activePage.fixedRows.push(activePage.currentRow); From aba16687b0723a6cc5f13f921dd93011ca69c050 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 26 Aug 2023 10:23:09 -0700 Subject: [PATCH 14/24] Fix high pressure texture atlas corruption The corruption was caused by confusion with page indexes when merging pages. The fix is to simplify it by deleting all pages and then just adding the new page. Fixes #4534 Fixes #4351 --- src/browser/renderer/shared/TextureAtlas.ts | 83 ++++++++++----------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/src/browser/renderer/shared/TextureAtlas.ts b/src/browser/renderer/shared/TextureAtlas.ts index 4444d8f6..dd059574 100644 --- a/src/browser/renderer/shared/TextureAtlas.ts +++ b/src/browser/renderer/shared/TextureAtlas.ts @@ -152,49 +152,47 @@ export class TextureAtlas implements ITextureAtlas { // animation frame which would result in blank rendered areas. This is actually not that // expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. if (TextureAtlas.maxAtlasPages && this._pages.length >= Math.max(4, TextureAtlas.maxAtlasPages)) { - queueMicrotask(() => { - // Find the set of the largest 4 images, below the maximum size, with the highest - // percentages used - const pagesBySize = this._pages.filter(e => { - return e.canvas.width * 2 <= (TextureAtlas.maxTextureSize || Constants.FORCED_MAX_TEXTURE_SIZE); - }).sort((a, b) => { - if (b.canvas.width !== a.canvas.width) { - return b.canvas.width - a.canvas.width; - } - return b.percentageUsed - a.percentageUsed; - }); - let sameSizeI = -1; - let size = 0; - for (let i = 0; i < pagesBySize.length; i++) { - if (pagesBySize[i].canvas.width !== size) { - sameSizeI = i; - size = pagesBySize[i].canvas.width; - } else if (i - sameSizeI === 3) { - break; - } + // Find the set of the largest 4 images, below the maximum size, with the highest + // percentages used + const pagesBySize = this._pages.filter(e => { + return e.canvas.width * 2 <= (TextureAtlas.maxTextureSize || Constants.FORCED_MAX_TEXTURE_SIZE); + }).sort((a, b) => { + if (b.canvas.width !== a.canvas.width) { + return b.canvas.width - a.canvas.width; } - - // Gather details of the merge - const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4); - const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a > b ? 1 : -1); - const mergedPageIndex = sortedMergingPagesIndexes[0]; - - // Merge into the new page - const mergedPage = this._mergePages(mergingPages, mergedPageIndex); - mergedPage.version++; - - // Replace the first _merging_ page with the _merged_ page - this._pages[mergedPageIndex] = mergedPage; - - // Delete the other 3 pages, shifting glyph texture pages as needed - for (let i = sortedMergingPagesIndexes.length - 1; i >= 1; i--) { - this._deletePage(sortedMergingPagesIndexes[i]); - } - - // Request the model to be cleared to refresh all texture pages. - this._requestClearModel = true; - this._onAddTextureAtlasCanvas.fire(mergedPage.canvas); + return b.percentageUsed - a.percentageUsed; }); + let sameSizeI = -1; + let size = 0; + for (let i = 0; i < pagesBySize.length; i++) { + if (pagesBySize[i].canvas.width !== size) { + sameSizeI = i; + size = pagesBySize[i].canvas.width; + } else if (i - sameSizeI === 3) { + break; + } + } + + // Gather details of the merge + const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4); + const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a > b ? 1 : -1); + const mergedPageIndex = this.pages.length - mergingPages.length; + + // Merge into the new page + const mergedPage = this._mergePages(mergingPages, mergedPageIndex); + mergedPage.version++; + + // Delete the pages, shifting glyph texture pages as needed + for (let i = sortedMergingPagesIndexes.length - 1; i >= 0; i--) { + this._deletePage(sortedMergingPagesIndexes[i]); + } + + // Add the new merged page to the end + this.pages.push(mergedPage); + + // Request the model to be cleared to refresh all texture pages. + this._requestClearModel = true; + this._onAddTextureAtlasCanvas.fire(mergedPage.canvas); } // All new atlas pages are created small as they are highly dynamic @@ -780,7 +778,8 @@ export class TextureAtlas implements ITextureAtlas { // improve texture utilization by using the available space before the page is merged // and becomes static. if ( - TextureAtlas.maxAtlasPages === this._pages.length && + TextureAtlas.maxAtlasPages && + this._pages.length >= TextureAtlas.maxAtlasPages && activeRow.y + rasterizedGlyph.size.y <= activePage.canvas.height && activeRow.height >= rasterizedGlyph.size.y && activeRow.x + rasterizedGlyph.size.x <= activePage.canvas.width From 1e1ccba238d256eea15b46162263cc052f9cca45 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 27 Aug 2023 06:44:11 -0700 Subject: [PATCH 15/24] Use MutableDisposable in BaseRenderLayer --- .../xterm-addon-canvas/src/BaseRenderLayer.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/addons/xterm-addon-canvas/src/BaseRenderLayer.ts b/addons/xterm-addon-canvas/src/BaseRenderLayer.ts index ff29bbf1..6c1335c8 100644 --- a/addons/xterm-addon-canvas/src/BaseRenderLayer.ts +++ b/addons/xterm-addon-canvas/src/BaseRenderLayer.ts @@ -3,24 +3,24 @@ * @license MIT */ +import { ReadonlyColorSet } from 'browser/Types'; +import { CellColorResolver } from 'browser/renderer/shared/CellColorResolver'; import { acquireTextureAtlas } from 'browser/renderer/shared/CharAtlasCache'; import { TEXT_BASELINE } from 'browser/renderer/shared/Constants'; import { tryDrawCustomChar } from 'browser/renderer/shared/CustomGlyphs'; import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; -import { IRasterizedGlyph, IRenderDimensions, ISelectionRenderModel, ITextureAtlas } from 'browser/renderer/shared/Types'; import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel'; +import { IRasterizedGlyph, IRenderDimensions, ISelectionRenderModel, ITextureAtlas } from 'browser/renderer/shared/Types'; import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; -import { ReadonlyColorSet } from 'browser/Types'; +import { EventEmitter, forwardEvent } from 'common/EventEmitter'; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; +import { isSafari } from 'common/Platform'; +import { ICellData } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; import { WHITESPACE_CELL_CODE } from 'common/buffer/Constants'; import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services'; -import { ICellData, IDisposable } from 'common/Types'; import { Terminal } from 'xterm'; import { IRenderLayer } from './Types'; -import { CellColorResolver } from 'browser/renderer/shared/CellColorResolver'; -import { Disposable, toDisposable } from 'common/Lifecycle'; -import { isSafari } from 'common/Platform'; -import { EventEmitter, forwardEvent } from 'common/EventEmitter'; export abstract class BaseRenderLayer extends Disposable implements IRenderLayer { private _canvas: HTMLCanvasElement; @@ -37,7 +37,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer private _bitmapGenerator: (BitmapGenerator | undefined)[] = []; protected _charAtlas!: ITextureAtlas; - private _charAtlasDisposable?: IDisposable; + protected _charAtlasDisposable = this.register(new MutableDisposable()); public get canvas(): HTMLCanvasElement { return this._canvas; } public get cacheCanvas(): HTMLCanvasElement { return this._charAtlas?.pages[0].canvas!; } @@ -122,9 +122,8 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer if (this._deviceCharWidth <= 0 && this._deviceCharHeight <= 0) { return; } - this._charAtlasDisposable?.dispose(); this._charAtlas = acquireTextureAtlas(this._terminal, this._optionsService.rawOptions, colorSet, this._deviceCellWidth, this._deviceCellHeight, this._deviceCharWidth, this._deviceCharHeight, this._coreBrowserService.dpr); - this._charAtlasDisposable = forwardEvent(this._charAtlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas); + this._charAtlasDisposable.value = forwardEvent(this._charAtlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas); this._charAtlas.warmUp(); for (let i = 0; i < this._charAtlas.pages.length; i++) { this._bitmapGenerator[i] = new BitmapGenerator(this._charAtlas.pages[i].canvas); From 94ed3e800a2cb9d3afd0309c2165ba4d1b8a2652 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 27 Aug 2023 06:44:48 -0700 Subject: [PATCH 16/24] Don't dispose of texture atlas in canvas addon This would cause problems if the atlas is shared between multiple terminals. --- addons/xterm-addon-canvas/src/BaseRenderLayer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/xterm-addon-canvas/src/BaseRenderLayer.ts b/addons/xterm-addon-canvas/src/BaseRenderLayer.ts index 6c1335c8..8c199336 100644 --- a/addons/xterm-addon-canvas/src/BaseRenderLayer.ts +++ b/addons/xterm-addon-canvas/src/BaseRenderLayer.ts @@ -74,7 +74,6 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer this.register(toDisposable(() => { this._canvas.remove(); - this._charAtlas?.dispose(); })); } From ec02bab97c40e3c9d70a4cd7710967437cfd53b8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 27 Aug 2023 06:46:49 -0700 Subject: [PATCH 17/24] Use MutableDisposable in CursorRenderLayer --- .../src/CursorRenderLayer.ts | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/addons/xterm-addon-canvas/src/CursorRenderLayer.ts b/addons/xterm-addon-canvas/src/CursorRenderLayer.ts index 9f4ac896..c5174f86 100644 --- a/addons/xterm-addon-canvas/src/CursorRenderLayer.ts +++ b/addons/xterm-addon-canvas/src/CursorRenderLayer.ts @@ -7,7 +7,7 @@ import { CursorBlinkStateManager } from 'browser/renderer/shared/CursorBlinkStat import { IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/shared/Types'; import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { IEventEmitter } from 'common/EventEmitter'; -import { toDisposable } from 'common/Lifecycle'; +import { MutableDisposable } from 'common/Lifecycle'; import { isFirefox } from 'common/Platform'; import { ICellData } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; @@ -26,7 +26,7 @@ interface ICursorState { export class CursorRenderLayer extends BaseRenderLayer { private _state: ICursorState; private _cursorRenderers: {[key: string]: (x: number, y: number, cell: ICellData) => void}; - private _cursorBlinkStateManager: CursorBlinkStateManager | undefined; + private _cursorBlinkStateManager: MutableDisposable = this.register(new MutableDisposable()); private _cell: ICellData = new CellData(); constructor( @@ -57,10 +57,6 @@ export class CursorRenderLayer extends BaseRenderLayer { }; this.register(optionsService.onOptionChange(() => this._handleOptionsChanged())); this._handleOptionsChanged(); - this.register(toDisposable(() => { - this._cursorBlinkStateManager?.dispose(); - this._cursorBlinkStateManager = undefined; - })); } public resize(dim: IRenderDimensions): void { @@ -77,28 +73,27 @@ export class CursorRenderLayer extends BaseRenderLayer { public reset(): void { this._clearCursor(); - this._cursorBlinkStateManager?.restartBlinkAnimation(); + this._cursorBlinkStateManager.value?.restartBlinkAnimation(); this._handleOptionsChanged(); } public handleBlur(): void { - this._cursorBlinkStateManager?.pause(); + this._cursorBlinkStateManager.value?.pause(); this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y }); } public handleFocus(): void { - this._cursorBlinkStateManager?.resume(); + this._cursorBlinkStateManager.value?.resume(); this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y }); } private _handleOptionsChanged(): void { if (this._optionsService.rawOptions.cursorBlink) { - if (!this._cursorBlinkStateManager) { - this._cursorBlinkStateManager = new CursorBlinkStateManager(() => this._render(true), this._coreBrowserService); + if (!this._cursorBlinkStateManager.value) { + this._cursorBlinkStateManager.value = new CursorBlinkStateManager(() => this._render(true), this._coreBrowserService); } } else { - this._cursorBlinkStateManager?.dispose(); - this._cursorBlinkStateManager = undefined; + this._cursorBlinkStateManager.clear(); } // Request a refresh from the terminal as management of rendering is being // moved back to the terminal @@ -106,14 +101,14 @@ export class CursorRenderLayer extends BaseRenderLayer { } public handleCursorMove(): void { - this._cursorBlinkStateManager?.restartBlinkAnimation(); + this._cursorBlinkStateManager.value?.restartBlinkAnimation(); } public handleGridChanged(startRow: number, endRow: number): void { - if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isPaused) { + if (!this._cursorBlinkStateManager.value || this._cursorBlinkStateManager.value.isPaused) { this._render(false); } else { - this._cursorBlinkStateManager.restartBlinkAnimation(); + this._cursorBlinkStateManager.value.restartBlinkAnimation(); } } @@ -159,7 +154,7 @@ export class CursorRenderLayer extends BaseRenderLayer { } // Don't draw the cursor if it's blinking - if (this._cursorBlinkStateManager && !this._cursorBlinkStateManager.isCursorVisible) { + if (this._cursorBlinkStateManager.value && !this._cursorBlinkStateManager.value.isCursorVisible) { this._clearCursor(); return; } From 57db72d287f76632f0b4464b7668e7fe317fa6d2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 27 Aug 2023 06:55:16 -0700 Subject: [PATCH 18/24] More adoption of MutableDisposable --- addons/xterm-addon-search/src/SearchAddon.ts | 19 +++----- addons/xterm-addon-webgl/src/WebglRenderer.ts | 45 +++++++++---------- .../src/renderLayer/BaseRenderLayer.ts | 1 - src/browser/Terminal.ts | 13 +++--- 4 files changed, 34 insertions(+), 44 deletions(-) diff --git a/addons/xterm-addon-search/src/SearchAddon.ts b/addons/xterm-addon-search/src/SearchAddon.ts index cbde1a1b..10fc1ecc 100644 --- a/addons/xterm-addon-search/src/SearchAddon.ts +++ b/addons/xterm-addon-search/src/SearchAddon.ts @@ -5,7 +5,7 @@ import { Terminal, IDisposable, ITerminalAddon, IDecoration } from 'xterm'; import { EventEmitter } from 'common/EventEmitter'; -import { Disposable, toDisposable, disposeArray } from 'common/Lifecycle'; +import { Disposable, toDisposable, disposeArray, MutableDisposable } from 'common/Lifecycle'; export interface ISearchOptions { regex?: boolean; @@ -66,7 +66,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon { private _cachedSearchTerm: string | undefined; private _highlightedLines: Set = new Set(); private _highlightDecorations: IHighlight[] = []; - private _selectedDecoration: IHighlight | undefined; + private _selectedDecoration: MutableDisposable = this.register(new MutableDisposable()); private _highlightLimit: number; private _lastSearchOptions: ISearchOptions | undefined; private _highlightTimeout: number | undefined; @@ -110,7 +110,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon { } public clearDecorations(retainCachedSearchTerm?: boolean): void { - this.clearActiveDecoration(); + this._selectedDecoration.clear(); disposeArray(this._highlightDecorations); this._highlightDecorations = []; this._highlightedLines.clear(); @@ -119,11 +119,6 @@ export class SearchAddon extends Disposable implements ITerminalAddon { } } - public clearActiveDecoration(): void { - this._selectedDecoration?.dispose(); - this._selectedDecoration = undefined; - } - /** * Find the next instance of the term, then scroll to and select it. If it * doesn't exist, do nothing. @@ -320,8 +315,8 @@ export class SearchAddon extends Disposable implements ITerminalAddon { private _fireResults(searchOptions?: ISearchOptions): void { if (searchOptions?.decorations) { let resultIndex = -1; - if (this._selectedDecoration) { - const selectedMatch = this._selectedDecoration.match; + if (this._selectedDecoration.value) { + const selectedMatch = this._selectedDecoration.value.match; for (let i = 0; i < this._highlightDecorations.length; i++) { const match = this._highlightDecorations[i].match; if (match.row === selectedMatch.row && match.col === selectedMatch.col && match.size === selectedMatch.size) { @@ -642,7 +637,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon { */ private _selectResult(result: ISearchResult | undefined, options?: ISearchDecorationOptions, noScroll?: boolean): boolean { const terminal = this._terminal!; - this.clearActiveDecoration(); + this._selectedDecoration.clear(); if (!result) { terminal.clearSelection(); return false; @@ -666,7 +661,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon { disposables.push(marker); disposables.push(decoration.onRender((e) => this._applyStyles(e, options.activeMatchBorder, true))); disposables.push(decoration.onDispose(() => disposeArray(disposables))); - this._selectedDecoration = { decoration, match: result, dispose() { decoration.dispose(); } }; + this._selectedDecoration.value = { decoration, match: result, dispose() { decoration.dispose(); } }; } } } diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 20db5faf..c57565a3 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -41,8 +41,8 @@ export class WebglRenderer extends Disposable implements IRenderer { private _canvas: HTMLCanvasElement; private _gl: IWebGL2RenderingContext; - private _rectangleRenderer?: RectangleRenderer; - private _glyphRenderer?: GlyphRenderer; + private _rectangleRenderer: MutableDisposable = this.register(new MutableDisposable()); + private _glyphRenderer: MutableDisposable = this.register(new MutableDisposable()); public readonly dimensions: IRenderDimensions; @@ -128,7 +128,7 @@ export class WebglRenderer extends Disposable implements IRenderer { this._core.screenElement!.appendChild(this._canvas); - [this._rectangleRenderer, this._glyphRenderer] = this._initializeWebGLState(); + [this._rectangleRenderer.value, this._glyphRenderer.value] = this._initializeWebGLState(); this._isAttached = this._coreBrowserService.window.document.body.contains(this._core.screenElement!); @@ -182,10 +182,10 @@ export class WebglRenderer extends Disposable implements IRenderer { this._core.screenElement!.style.width = `${this.dimensions.css.canvas.width}px`; this._core.screenElement!.style.height = `${this.dimensions.css.canvas.height}px`; - this._rectangleRenderer?.setDimensions(this.dimensions); - this._rectangleRenderer?.handleResize(); - this._glyphRenderer?.setDimensions(this.dimensions); - this._glyphRenderer?.handleResize(); + this._rectangleRenderer.value?.setDimensions(this.dimensions); + this._rectangleRenderer.value?.handleResize(); + this._glyphRenderer.value?.setDimensions(this.dimensions); + this._glyphRenderer.value?.handleResize(); this._refreshCharAtlas(); @@ -241,17 +241,14 @@ export class WebglRenderer extends Disposable implements IRenderer { * Initializes members dependent on WebGL context state. */ private _initializeWebGLState(): [RectangleRenderer, GlyphRenderer] { - // Dispose any previous rectangle and glyph renderers before creating new ones. - this._rectangleRenderer?.dispose(); - this._glyphRenderer?.dispose(); - - this._rectangleRenderer = this.register(new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService)); - this._glyphRenderer = this.register(new GlyphRenderer(this._terminal, this._gl, this.dimensions)); + this._rectangleRenderer.value = new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService); + this._glyphRenderer.value = new GlyphRenderer(this._terminal, this._gl, this.dimensions); // Update dimensions and acquire char atlas this.handleCharSizeChanged(); - return [this._rectangleRenderer, this._glyphRenderer]; + return [this._rectangleRenderer.value, this._glyphRenderer.value + ]; } /** @@ -284,7 +281,7 @@ export class WebglRenderer extends Disposable implements IRenderer { } this._charAtlas = atlas; this._charAtlas.warmUp(); - this._glyphRenderer?.setAtlas(this._charAtlas); + this._glyphRenderer.value?.setAtlas(this._charAtlas); } /** @@ -340,14 +337,14 @@ export class WebglRenderer extends Disposable implements IRenderer { l.handleGridChanged(this._terminal, start, end); } - if (!this._glyphRenderer || !this._rectangleRenderer) { + if (!this._glyphRenderer.value || !this._rectangleRenderer.value) { return; } // Tell renderer the frame is beginning // upon a model clear also refresh the full viewport model // (also triggered by an atlas page merge, part of #4480) - if (this._glyphRenderer.beginFrame()) { + if (this._glyphRenderer.value.beginFrame()) { this._clearModel(true); this._updateModel(0, this._terminal.rows - 1); } else { @@ -356,10 +353,10 @@ export class WebglRenderer extends Disposable implements IRenderer { } // Render - this._rectangleRenderer?.renderBackgrounds(); - this._glyphRenderer?.render(this._model); + this._rectangleRenderer.value.renderBackgrounds(); + this._glyphRenderer.value.render(this._model); if (!this._cursorBlinkStateManager.value || this._cursorBlinkStateManager.value.isCursorVisible) { - this._rectangleRenderer?.renderCursor(); + this._rectangleRenderer.value.renderCursor(); } } @@ -502,7 +499,7 @@ export class WebglRenderer extends Disposable implements IRenderer { this._model.cells[i + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg; this._model.cells[i + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext; - this._glyphRenderer!.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, lastBg); + this._glyphRenderer.value!.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, lastBg); if (isJoined) { // Restore work cell @@ -511,7 +508,7 @@ export class WebglRenderer extends Disposable implements IRenderer { // Null out non-first cells for (x++; x < lastCharX; x++) { j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL; - this._glyphRenderer!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0); + this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0); this._model.cells[j] = NULL_CELL_CODE; this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._cellColorResolver.result.bg; this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg; @@ -521,9 +518,9 @@ export class WebglRenderer extends Disposable implements IRenderer { } } if (modelUpdated) { - this._rectangleRenderer!.updateBackgrounds(this._model); + this._rectangleRenderer.value!.updateBackgrounds(this._model); } - this._rectangleRenderer!.updateCursor(this._model); + this._rectangleRenderer.value!.updateCursor(this._model); } /** diff --git a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts index f5449c4d..3aaac435 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts @@ -49,7 +49,6 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer })); this.register(toDisposable(() => { this._canvas.remove(); - this._charAtlas?.dispose(); })); } diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index 24dace2f..a092e1bc 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -44,7 +44,7 @@ import { ThemeService } from 'browser/services/ThemeService'; import { color, rgba } from 'common/Color'; import { CoreTerminal } from 'common/CoreTerminal'; import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter'; -import { toDisposable } from 'common/Lifecycle'; +import { MutableDisposable, toDisposable } from 'common/Lifecycle'; import * as Browser from 'common/Platform'; import { ColorRequestType, CoreMouseAction, CoreMouseButton, CoreMouseEventType, IColorEvent, ITerminalOptions, KeyboardResultType, ScrollSource, SpecialColorIndex } from 'common/Types'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; @@ -118,7 +118,7 @@ export class Terminal extends CoreTerminal implements ITerminal { public linkifier2: ILinkifier2; public viewport: IViewport | undefined; private _compositionHelper: ICompositionHelper | undefined; - private _accessibilityManager: AccessibilityManager | undefined; + private _accessibilityManager: MutableDisposable = this.register(new MutableDisposable()); private readonly _onCursorMove = this.register(new EventEmitter()); public readonly onCursorMove = this._onCursorMove.event; @@ -252,12 +252,11 @@ export class Terminal extends CoreTerminal implements ITerminal { private _handleScreenReaderModeOptionChange(value: boolean): void { if (value) { - if (!this._accessibilityManager && this._renderService) { - this._accessibilityManager = this._instantiationService.createInstance(AccessibilityManager, this); + if (!this._accessibilityManager.value && this._renderService) { + this._accessibilityManager.value = this._instantiationService.createInstance(AccessibilityManager, this); } } else { - this._accessibilityManager?.dispose(); - this._accessibilityManager = undefined; + this._accessibilityManager.clear(); } } @@ -535,7 +534,7 @@ export class Terminal extends CoreTerminal implements ITerminal { if (this.options.screenReaderMode) { // Note that this must be done *after* the renderer is created in order to // ensure the correct order of the dprchange event - this._accessibilityManager = this._instantiationService.createInstance(AccessibilityManager, this); + this._accessibilityManager.value = this._instantiationService.createInstance(AccessibilityManager, this); } this.register(this.optionsService.onSpecificOptionChange('screenReaderMode', e => this._handleScreenReaderModeOptionChange(e))); From 37b0e4018cd73d3b32ab89cfcd2be62a40585944 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 27 Aug 2023 07:03:27 -0700 Subject: [PATCH 19/24] More adoption of MutableDisposable --- addons/xterm-addon-image/src/ImageRenderer.ts | 6 +- src/browser/services/RenderService.ts | 58 +++++++++---------- src/common/CoreTerminal.ts | 16 ++--- 3 files changed, 35 insertions(+), 45 deletions(-) diff --git a/addons/xterm-addon-image/src/ImageRenderer.ts b/addons/xterm-addon-image/src/ImageRenderer.ts index d225315e..392816dc 100644 --- a/addons/xterm-addon-image/src/ImageRenderer.ts +++ b/addons/xterm-addon-image/src/ImageRenderer.ts @@ -6,6 +6,7 @@ import { toRGBA8888 } from 'sixel/lib/Colors'; import { IDisposable } from 'xterm'; import { ICellSize, ITerminalExt, IImageSpec, IRenderDimensions, IRenderService } from './Types'; +import { MutableDisposable } from 'common/Lifecycle'; const PLACEHOLDER_LENGTH = 4096; @@ -22,7 +23,7 @@ export class ImageRenderer implements IDisposable { private _ctx: CanvasRenderingContext2D | null | undefined; private _placeholder: HTMLCanvasElement | undefined; private _placeholderBitmap: ImageBitmap | undefined; - private _optionsRefresh: IDisposable | undefined; + private _optionsRefresh = new MutableDisposable(); private _oldOpen: ((parent: HTMLElement) => void) | undefined; private _renderService: IRenderService | undefined; private _oldSetRenderer: ((renderer: any) => void) | undefined; @@ -77,7 +78,7 @@ export class ImageRenderer implements IDisposable { this._open(); } // hack to spot fontSize changes - this._optionsRefresh = this._terminal._core.optionsService.onOptionChange(option => { + this._optionsRefresh.value = this._terminal._core.optionsService.onOptionChange(option => { if (option === 'fontSize') { this.rescaleCanvas(); this._renderService?.refreshRows(0, this._terminal.rows); @@ -87,7 +88,6 @@ export class ImageRenderer implements IDisposable { public dispose(): void { - this._optionsRefresh?.dispose(); this.removeLayerFromDom(); if (this._terminal._core && this._oldOpen) { this._terminal._core.open = this._oldOpen; diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 41f0c9b5..0f18a233 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -10,7 +10,7 @@ import { IRenderDebouncerWithCallback } from 'browser/Types'; import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types'; import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; import { EventEmitter } from 'common/EventEmitter'; -import { Disposable } from 'common/Lifecycle'; +import { Disposable, MutableDisposable } from 'common/Lifecycle'; import { DebouncedIdleTask } from 'common/TaskQueue'; import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services'; @@ -23,7 +23,7 @@ interface ISelectionState { export class RenderService extends Disposable implements IRenderService { public serviceBrand: undefined; - private _renderer: IRenderer | undefined; + private _renderer: MutableDisposable = this.register(new MutableDisposable()); private _renderDebouncer: IRenderDebouncerWithCallback; private _screenDprMonitor: ScreenDprMonitor; private _pausedResizeTask = new DebouncedIdleTask(); @@ -49,7 +49,7 @@ export class RenderService extends Disposable implements IRenderService { private readonly _onRefreshRequest = this.register(new EventEmitter<{ start: number, end: number }>()); public readonly onRefreshRequest = this._onRefreshRequest.event; - public get dimensions(): IRenderDimensions { return this._renderer!.dimensions; } + public get dimensions(): IRenderDimensions { return this._renderer.value!.dimensions; } constructor( private _rowCount: number, @@ -63,8 +63,6 @@ export class RenderService extends Disposable implements IRenderService { ) { super(); - this.register({ dispose: () => this._renderer?.dispose() }); - this._renderDebouncer = new RenderDebouncer(coreBrowserService.window, (start, end) => this._renderRows(start, end)); this.register(this._renderDebouncer); @@ -73,7 +71,7 @@ export class RenderService extends Disposable implements IRenderService { this.register(this._screenDprMonitor); this.register(bufferService.onResize(() => this._fullRefresh())); - this.register(bufferService.buffers.onBufferActivate(() => this._renderer?.clear())); + this.register(bufferService.buffers.onBufferActivate(() => this._renderer.value?.clear())); this.register(optionsService.onOptionChange(() => this._handleOptionsChanged())); this.register(this._charSizeService.onCharSizeChange(() => this.handleCharSizeChanged())); @@ -148,7 +146,7 @@ export class RenderService extends Disposable implements IRenderService { } private _renderRows(start: number, end: number): void { - if (!this._renderer) { + if (!this._renderer.value) { return; } @@ -159,11 +157,11 @@ export class RenderService extends Disposable implements IRenderService { end = Math.min(end, this._rowCount - 1); // Render - this._renderer.renderRows(start, end); + this._renderer.value.renderRows(start, end); // Update selection if needed if (this._needsSelectionRefresh) { - this._renderer.handleSelectionChanged(this._selectionState.start, this._selectionState.end, this._selectionState.columnSelectMode); + this._renderer.value.handleSelectionChanged(this._selectionState.start, this._selectionState.end, this._selectionState.columnSelectMode); this._needsSelectionRefresh = false; } @@ -181,7 +179,7 @@ export class RenderService extends Disposable implements IRenderService { } private _handleOptionsChanged(): void { - if (!this._renderer) { + if (!this._renderer.value) { return; } this.refreshRows(0, this._rowCount - 1); @@ -189,25 +187,23 @@ export class RenderService extends Disposable implements IRenderService { } private _fireOnCanvasResize(): void { - if (!this._renderer) { + if (!this._renderer.value) { return; } // Don't fire the event if the dimensions haven't changed - if (this._renderer.dimensions.css.canvas.width === this._canvasWidth && this._renderer.dimensions.css.canvas.height === this._canvasHeight) { + if (this._renderer.value.dimensions.css.canvas.width === this._canvasWidth && this._renderer.value.dimensions.css.canvas.height === this._canvasHeight) { return; } - this._onDimensionsChange.fire(this._renderer.dimensions); + this._onDimensionsChange.fire(this._renderer.value.dimensions); } public hasRenderer(): boolean { - return !!this._renderer; + return !!this._renderer.value; } public setRenderer(renderer: IRenderer): void { - // TODO: RenderService should be the only one to dispose the renderer - this._renderer?.dispose(); - this._renderer = renderer; - this._renderer.onRequestRedraw(e => this.refreshRows(e.start, e.end, true)); + this._renderer.value = renderer; + this._renderer.value.onRequestRedraw(e => this.refreshRows(e.start, e.end, true)); // Force a refresh this._needsSelectionRefresh = true; @@ -227,10 +223,10 @@ export class RenderService extends Disposable implements IRenderService { } public clearTextureAtlas(): void { - if (!this._renderer) { + if (!this._renderer.value) { return; } - this._renderer.clearTextureAtlas?.(); + this._renderer.value.clearTextureAtlas?.(); this._fullRefresh(); } @@ -239,50 +235,50 @@ export class RenderService extends Disposable implements IRenderService { // when devicePixelRatio changes this._charSizeService.measure(); - if (!this._renderer) { + if (!this._renderer.value) { return; } - this._renderer.handleDevicePixelRatioChange(); + this._renderer.value.handleDevicePixelRatioChange(); this.refreshRows(0, this._rowCount - 1); } public handleResize(cols: number, rows: number): void { - if (!this._renderer) { + if (!this._renderer.value) { return; } if (this._isPaused) { - this._pausedResizeTask.set(() => this._renderer!.handleResize(cols, rows)); + this._pausedResizeTask.set(() => this._renderer.value!.handleResize(cols, rows)); } else { - this._renderer.handleResize(cols, rows); + this._renderer.value.handleResize(cols, rows); } this._fullRefresh(); } // TODO: Is this useful when we have onResize? public handleCharSizeChanged(): void { - this._renderer?.handleCharSizeChanged(); + this._renderer.value?.handleCharSizeChanged(); } public handleBlur(): void { - this._renderer?.handleBlur(); + this._renderer.value?.handleBlur(); } public handleFocus(): void { - this._renderer?.handleFocus(); + this._renderer.value?.handleFocus(); } public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void { this._selectionState.start = start; this._selectionState.end = end; this._selectionState.columnSelectMode = columnSelectMode; - this._renderer?.handleSelectionChanged(start, end, columnSelectMode); + this._renderer.value?.handleSelectionChanged(start, end, columnSelectMode); } public handleCursorMove(): void { - this._renderer?.handleCursorMove(); + this._renderer.value?.handleCursorMove(); } public clear(): void { - this._renderer?.clear(); + this._renderer.value?.clear(); } } diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 89d9f5e5..47f77406 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -21,7 +21,7 @@ * http://linux.die.net/man/7/urxvt */ -import { Disposable, toDisposable } from 'common/Lifecycle'; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, ICoreMouseService, IUnicodeService, LogLevelEnum, ITerminalOptions, IOscLinkService } from 'common/services/Services'; import { InstantiationService } from 'common/services/InstantiationService'; import { LogService } from 'common/services/LogService'; @@ -57,7 +57,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { protected _inputHandler: InputHandler; private _writeBuffer: WriteBuffer; - private _windowsWrappingHeuristics: IDisposable | undefined; + private _windowsWrappingHeuristics = this.register(new MutableDisposable()); private readonly _onBinary = this.register(new EventEmitter()); public readonly onBinary = this._onBinary.event; @@ -144,11 +144,6 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { // Setup WriteBuffer this._writeBuffer = this.register(new WriteBuffer((data, promiseResult) => this._inputHandler.parse(data, promiseResult))); this.register(forwardEvent(this._writeBuffer.onWriteParsed, this._onWriteParsed)); - - this.register(toDisposable(() => { - this._windowsWrappingHeuristics?.dispose(); - this._windowsWrappingHeuristics = undefined; - })); } public write(data: string | Uint8Array, callback?: () => void): void { @@ -267,20 +262,19 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { if (value) { this._enableWindowsWrappingHeuristics(); } else { - this._windowsWrappingHeuristics?.dispose(); - this._windowsWrappingHeuristics = undefined; + this._windowsWrappingHeuristics.clear(); } } protected _enableWindowsWrappingHeuristics(): void { - if (!this._windowsWrappingHeuristics) { + if (!this._windowsWrappingHeuristics.value) { const disposables: IDisposable[] = []; disposables.push(this.onLineFeed(updateWindowsModeWrappedState.bind(null, this._bufferService))); disposables.push(this.registerCsiHandler({ final: 'H' }, () => { updateWindowsModeWrappedState(this._bufferService); return false; })); - this._windowsWrappingHeuristics = toDisposable(() => { + this._windowsWrappingHeuristics.value = toDisposable(() => { for (const d of disposables) { d.dispose(); } From 2eaa9b3b691d35ebf8bcef32b1fe1f7fa86df966 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 27 Aug 2023 07:15:51 -0700 Subject: [PATCH 20/24] Fix remaining issues with MutableDisposable usage --- addons/xterm-addon-image/src/ImageRenderer.ts | 43 +++++++++---------- addons/xterm-addon-webgl/src/WebglRenderer.ts | 12 +++--- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/addons/xterm-addon-image/src/ImageRenderer.ts b/addons/xterm-addon-image/src/ImageRenderer.ts index 392816dc..9b528efe 100644 --- a/addons/xterm-addon-image/src/ImageRenderer.ts +++ b/addons/xterm-addon-image/src/ImageRenderer.ts @@ -6,7 +6,7 @@ import { toRGBA8888 } from 'sixel/lib/Colors'; import { IDisposable } from 'xterm'; import { ICellSize, ITerminalExt, IImageSpec, IRenderDimensions, IRenderService } from './Types'; -import { MutableDisposable } from 'common/Lifecycle'; +import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; const PLACEHOLDER_LENGTH = 4096; @@ -18,12 +18,12 @@ const PLACEHOLDER_HEIGHT = 24; * - add canvas layer to DOM (browser only for now) * - draw image tiles onRender */ -export class ImageRenderer implements IDisposable { +export class ImageRenderer extends Disposable implements IDisposable { public canvas: HTMLCanvasElement | undefined; private _ctx: CanvasRenderingContext2D | null | undefined; private _placeholder: HTMLCanvasElement | undefined; private _placeholderBitmap: ImageBitmap | undefined; - private _optionsRefresh = new MutableDisposable(); + private _optionsRefresh = this.register(new MutableDisposable()); private _oldOpen: ((parent: HTMLElement) => void) | undefined; private _renderService: IRenderService | undefined; private _oldSetRenderer: ((renderer: any) => void) | undefined; @@ -69,6 +69,7 @@ export class ImageRenderer implements IDisposable { constructor(private _terminal: ITerminalExt) { + super(); this._oldOpen = this._terminal._core.open; this._terminal._core.open = (parent: HTMLElement): void => { this._oldOpen?.call(this._terminal._core, parent); @@ -84,25 +85,23 @@ export class ImageRenderer implements IDisposable { this._renderService?.refreshRows(0, this._terminal.rows); } }); - } - - - public dispose(): void { - this.removeLayerFromDom(); - if (this._terminal._core && this._oldOpen) { - this._terminal._core.open = this._oldOpen; - this._oldOpen = undefined; - } - if (this._renderService && this._oldSetRenderer) { - this._renderService.setRenderer = this._oldSetRenderer; - this._oldSetRenderer = undefined; - } - this._renderService = undefined; - this.canvas = undefined; - this._ctx = undefined; - this._placeholderBitmap?.close(); - this._placeholderBitmap = undefined; - this._placeholder = undefined; + this.register(toDisposable(() => { + this.removeLayerFromDom(); + if (this._terminal._core && this._oldOpen) { + this._terminal._core.open = this._oldOpen; + this._oldOpen = undefined; + } + if (this._renderService && this._oldSetRenderer) { + this._renderService.setRenderer = this._oldSetRenderer; + this._oldSetRenderer = undefined; + } + this._renderService = undefined; + this.canvas = undefined; + this._ctx = undefined; + this._placeholderBitmap?.close(); + this._placeholderBitmap = undefined; + this._placeholder = undefined; + })); } /** diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index c57565a3..9a8f8ee3 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -20,7 +20,7 @@ import { CellData } from 'common/buffer/CellData'; import { Attributes, Content, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants'; import { traceCall } from 'common/services/LogService'; import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; -import { IDisposable, Terminal } from 'xterm'; +import { Terminal } from 'xterm'; import { GlyphRenderer } from './GlyphRenderer'; import { RectangleRenderer } from './RectangleRenderer'; import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_EXT_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL, RenderModel } from './RenderModel'; @@ -31,7 +31,7 @@ import { IRenderLayer } from './renderLayer/Types'; export class WebglRenderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; private _cursorBlinkStateManager: MutableDisposable = new MutableDisposable(); - private _charAtlasDisposable: IDisposable | undefined; + private _charAtlasDisposable = this.register(new MutableDisposable()); private _charAtlas: ITextureAtlas | undefined; private _devicePixelRatio: number; @@ -247,8 +247,7 @@ export class WebglRenderer extends Disposable implements IRenderer { // Update dimensions and acquire char atlas this.handleCharSizeChanged(); - return [this._rectangleRenderer.value, this._glyphRenderer.value - ]; + return [this._rectangleRenderer.value, this._glyphRenderer.value]; } /** @@ -272,9 +271,8 @@ export class WebglRenderer extends Disposable implements IRenderer { this._coreBrowserService.dpr ); if (this._charAtlas !== atlas) { - this._charAtlasDisposable?.dispose(); this._onChangeTextureAtlas.fire(atlas.pages[0].canvas); - this._charAtlasDisposable = getDisposeArrayDisposable([ + this._charAtlasDisposable.value = getDisposeArrayDisposable([ forwardEvent(atlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas), forwardEvent(atlas.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas) ]); @@ -292,7 +290,7 @@ export class WebglRenderer extends Disposable implements IRenderer { private _clearModel(clearGlyphRenderer: boolean): void { this._model.clear(); if (clearGlyphRenderer) { - this._glyphRenderer?.clear(); + this._glyphRenderer.value?.clear(); } } From 38c6af9b46ae6c721f6bed5c939ce33244246a2b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 27 Aug 2023 07:22:08 -0700 Subject: [PATCH 21/24] Fix _renderer access in api test --- addons/xterm-addon-webgl/test/WebglRenderer.api.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/xterm-addon-webgl/test/WebglRenderer.api.ts b/addons/xterm-addon-webgl/test/WebglRenderer.api.ts index 5d9cc0f0..24485cb2 100644 --- a/addons/xterm-addon-webgl/test/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/test/WebglRenderer.api.ts @@ -1106,7 +1106,7 @@ describe('WebGL Renderer Integration Tests', async () => { async function getCellColor(col: number, row: number): Promise { await page.evaluate(` - window.gl = window.term._core._renderService._renderer._gl; + window.gl = window.term._core._renderService._renderer.value._gl; window.result = new Uint8Array(4); window.d = window.term._core._renderService.dimensions; window.gl.readPixels( @@ -1120,7 +1120,7 @@ async function getCellColor(col: number, row: number): Promise { async function getCellPixels(col: number, row: number): Promise { await page.evaluate(` - window.gl = window.term._core._renderService._renderer._gl; + window.gl = window.term._core._renderService._renderer.value._gl; window.result = new Uint8Array(window.d.device.cell.width * window.d.device.cell.height * 4); window.d = window.term._core._renderService.dimensions; window.gl.readPixels( From 12a862c27702dde7d20ed72d06d80f2899ff5227 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 27 Aug 2023 07:28:04 -0700 Subject: [PATCH 22/24] Bring back unit test coverage report --- .github/workflows/ci.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75337542..99ca8331 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,6 +72,27 @@ jobs: - name: Lint API run: yarn lint-api + coverage: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v3 + - name: Use Node.js 18.x + uses: actions/setup-node@v3 + with: + node-version: 18.x + cache: 'yarn' + - name: Install dependencies + run: | + yarn --frozen-lockfile + yarn install-addons + - name: Unit test coverage + run: | + yarn test-unit-coverage --forbid-only + EXIT_CODE=$? + ./node_modules/.bin/nyc report --reporter=cobertura + exit $EXIT_CODE + test-unit-parallel: timeout-minutes: 20 strategy: From 03c1906f132625002deb72f933e73f20af16a3a3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 27 Aug 2023 07:33:46 -0700 Subject: [PATCH 23/24] Have coverage depend on build --- .github/workflows/ci.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99ca8331..e067459a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,20 +72,32 @@ jobs: - name: Lint API run: yarn lint-api - coverage: + test-unit-coverage: + needs: build runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v3 - - name: Use Node.js 18.x + - name: Use Node.js ${{ matrix.node-version }}.x uses: actions/setup-node@v3 with: - node-version: 18.x + node-version: ${{ matrix.node-version }}.x cache: 'yarn' - name: Install dependencies run: | yarn --frozen-lockfile yarn install-addons + - uses: actions/download-artifact@v3 + with: + name: build-artifacts + - name: Unzip artifacts (Linux, macOS) + if: runner.os != 'Windows' + run: unzip -o compressed-build.zip + - name: Unzip artifacts (Windows) + if: runner.os == 'Windows' + run: 7z x compressed-build.zip -aoa -o${{ github.workspace }} + - name: Print directory structure + run: ls -R - name: Unit test coverage run: | yarn test-unit-coverage --forbid-only From 923c28cb7711a266b9ca9bcdced6b988926a4179 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 27 Aug 2023 07:43:11 -0700 Subject: [PATCH 24/24] Use node 18 in coverage --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e067459a..2d3808c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,10 +78,10 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }}.x + - name: Use Node.js 18.x uses: actions/setup-node@v3 with: - node-version: ${{ matrix.node-version }}.x + node-version: 18.x cache: 'yarn' - name: Install dependencies run: |