Merge branch 'master' into perf3
This commit is contained in:
+6
-6
@@ -33,21 +33,21 @@ opening an issue, read these pointers.
|
|||||||
You can find issues to work on by looking at the [help wanted](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) or [good first issue](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) issues. It's a good idea to comment on the issue saying that you're taking it, just in case someone else comes along and you duplicate work. Once you have your issue, here are the steps to contribute:
|
You can find issues to work on by looking at the [help wanted](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) or [good first issue](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) issues. It's a good idea to comment on the issue saying that you're taking it, just in case someone else comes along and you duplicate work. Once you have your issue, here are the steps to contribute:
|
||||||
|
|
||||||
- Fork [xterm.js](https://github.com/sourcelair/xterm.js/)
|
- Fork [xterm.js](https://github.com/sourcelair/xterm.js/)
|
||||||
([how to fork a repo](https://help.github.com/articles/fork-a-repo))
|
([how to fork a repo](https://help.github.com/articles/fork-a-repo)).
|
||||||
- Get the [xterm.js demo](https://github.com/xtermjs/xterm.js/wiki/Contributing#running-the-demo) running
|
- Get the [xterm.js demo](https://github.com/xtermjs/xterm.js/wiki/Contributing#running-the-demo) running.
|
||||||
- Make your changes
|
- Make your changes.
|
||||||
- If your changes are easy to test or likely to regress, add tests. Tests go into `test`, directory.
|
- If your changes are easy to test or likely to regress, add tests. Tests go into `test`, directory.
|
||||||
- Follow the general code style of the rest of the project (see below).
|
- Follow the general code style of the rest of the project (see below).
|
||||||
- Submit a pull request
|
- Submit a pull request
|
||||||
([how to create a pull request](https://help.github.com/articles/fork-a-repo)).
|
([how to create a pull request](https://help.github.com/articles/fork-a-repo)).
|
||||||
Don't put more than one feature/fix in a single pull request.
|
Don't put more than one feature/fix in a single pull request.
|
||||||
|
|
||||||
By contributing code to xterm.js you
|
By contributing code to xterm.js you:
|
||||||
|
|
||||||
- agree to license the contributed code under xterm.js' [MIT
|
- Agree to license the contributed code under xterm.js' [MIT
|
||||||
license](LICENSE).
|
license](LICENSE).
|
||||||
|
|
||||||
- confirm that you have the right to contribute and license the code
|
- Confirm that you have the right to contribute and license the code
|
||||||
in question. (Either you hold all rights on the code, or the rights
|
in question. (Either you hold all rights on the code, or the rights
|
||||||
holder has explicitly granted the right to use it like this,
|
holder has explicitly granted the right to use it like this,
|
||||||
through a compatible open source license or through a direct
|
through a compatible open source license or through a direct
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2017-2018, The xterm.js authors (https://github.com/xtermjs/xterm.js)
|
Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
|
||||||
Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com)
|
Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com)
|
||||||
Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)
|
Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)
|
||||||
|
|
||||||
|
|||||||
@@ -7,17 +7,17 @@ Xterm.js is a front-end component written in TypeScript that lets applications b
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Terminal apps just work**: Xterm.js works with most terminal apps such as `bash`, `vim` and `tmux`, this includes support for curses-based apps and mouse event support
|
- **Terminal apps just work**: Xterm.js works with most terminal apps such as `bash`, `vim` and `tmux`, this includes support for curses-based apps and mouse event support.
|
||||||
- **Perfomant**: Xterm.js is *really* fast, it even includes a GPU-accelerated renderer
|
- **Perfomant**: Xterm.js is *really* fast, it even includes a GPU-accelerated renderer.
|
||||||
- **Rich unicode support**: Supports CJK, emojis and IMEs
|
- **Rich unicode support**: Supports CJK, emojis and IMEs.
|
||||||
- **Self-contained**: Requires zero dependencies to work
|
- **Self-contained**: Requires zero dependencies to work.
|
||||||
- **Accessible**: Screen reader support can be turned on using the `screenReaderMode` option
|
- **Accessible**: Screen reader support can be turned on using the `screenReaderMode` option.
|
||||||
- **And much more**: Links, theming, addons, well documented API, etc.
|
- **And much more**: Links, theming, addons, well documented API, etc.
|
||||||
|
|
||||||
## What xterm.js is not
|
## What xterm.js is not
|
||||||
|
|
||||||
- Xterm.js is not a terminal application that you can download and use on your computer
|
- Xterm.js is not a terminal application that you can download and use on your computer.
|
||||||
- Xterm.js is not `bash`. Xterm.js can be connected to processes like `bash` and let you interact with them (provide input, receive output)
|
- Xterm.js is not `bash`. Xterm.js can be connected to processes like `bash` and let you interact with them (provide input, receive output).
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ To start using xterm.js on your browser, add the `xterm.js` and `xterm.css` to t
|
|||||||
<script>
|
<script>
|
||||||
var term = new Terminal();
|
var term = new Terminal();
|
||||||
term.open(document.getElementById('terminal'));
|
term.open(document.getElementById('terminal'));
|
||||||
term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ')
|
term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ')
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -106,17 +106,17 @@ Note that some APIs are marked *experimental*, these are added to enable experim
|
|||||||
## Real-world uses
|
## Real-world uses
|
||||||
Xterm.js is used in several world-class applications to provide great terminal experiences.
|
Xterm.js is used in several world-class applications to provide great terminal experiences.
|
||||||
|
|
||||||
- [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js
|
- [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js.
|
||||||
- [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on xterm.js
|
- [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on xterm.js.
|
||||||
- [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js
|
- [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js.
|
||||||
- [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies.
|
- [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies.
|
||||||
- [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE.
|
- [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE.
|
||||||
- [**Codenvy**](http://www.codenvy.com): Cloud workspaces for development teams.
|
- [**Codenvy**](http://www.codenvy.com): Cloud workspaces for development teams.
|
||||||
- [**CoderPad**](https://coderpad.io): Online interviewing platform for programmers. Run code in many programming languages, with results displayed by `xterm.js`.
|
- [**CoderPad**](https://coderpad.io): Online interviewing platform for programmers. Run code in many programming languages, with results displayed by xterm.js.
|
||||||
- [**WebSSH2**](https://github.com/billchurch/WebSSH2): A web based SSH2 client using `xterm.js`, socket.io, and ssh2.
|
- [**WebSSH2**](https://github.com/billchurch/WebSSH2): A web based SSH2 client using xterm.js, socket.io, and ssh2.
|
||||||
- [**Spyder Terminal**](https://github.com/spyder-ide/spyder-terminal): A full fledged system terminal embedded on Spyder IDE.
|
- [**Spyder Terminal**](https://github.com/spyder-ide/spyder-terminal): A full fledged system terminal embedded on Spyder IDE.
|
||||||
- [**Cloud Commander**](https://cloudcmd.io "Cloud Commander"): Orthodox web file manager with console and editor.
|
- [**Cloud Commander**](https://cloudcmd.io "Cloud Commander"): Orthodox web file manager with console and editor.
|
||||||
- [**Codevolve**](https://www.codevolve.com "Codevolve"): Online platform for interactive coding and web development courses. Live container-backed terminal uses `xterm.js`.
|
- [**Codevolve**](https://www.codevolve.com "Codevolve"): Online platform for interactive coding and web development courses. Live container-backed terminal uses xterm.js.
|
||||||
- [**RStudio**](https://www.rstudio.com/products/RStudio "RStudio"): RStudio is an integrated development environment (IDE) for R.
|
- [**RStudio**](https://www.rstudio.com/products/RStudio "RStudio"): RStudio is an integrated development environment (IDE) for R.
|
||||||
- [**Terminal for Atom**](https://github.com/jsmecham/atom-terminal-tab): A simple terminal for the Atom text editor.
|
- [**Terminal for Atom**](https://github.com/jsmecham/atom-terminal-tab): A simple terminal for the Atom text editor.
|
||||||
- [**Eclipse Orion**](https://orionhub.org): A modern, open source software development environment that runs in the cloud. Code, deploy and run in the cloud.
|
- [**Eclipse Orion**](https://orionhub.org): A modern, open source software development environment that runs in the cloud. Code, deploy and run in the cloud.
|
||||||
@@ -124,9 +124,8 @@ Xterm.js is used in several world-class applications to provide great terminal e
|
|||||||
- [**Hexlet**](https://en.hexlet.io): Practical programming courses (JavaScript, PHP, Unix, databases, functional programming). A steady path from the first line of code to the first job.
|
- [**Hexlet**](https://en.hexlet.io): Practical programming courses (JavaScript, PHP, Unix, databases, functional programming). A steady path from the first line of code to the first job.
|
||||||
- [**Selenoid UI**](https://github.com/aerokube/selenoid-ui): Simple UI for the scallable golang implementation of Selenium Hub named Selenoid. We use XTerm for streaming logs over websockets from docker containers.
|
- [**Selenoid UI**](https://github.com/aerokube/selenoid-ui): Simple UI for the scallable golang implementation of Selenium Hub named Selenoid. We use XTerm for streaming logs over websockets from docker containers.
|
||||||
- [**Portainer**](https://portainer.io): Simple management UI for Docker.
|
- [**Portainer**](https://portainer.io): Simple management UI for Docker.
|
||||||
- [**SSHy**](https://github.com/stuicey/SSHy): HTML5 Based SSHv2 Web Client with E2E encryption utilising `xterm.js`, SJCL & websockets.
|
- [**SSHy**](https://github.com/stuicey/SSHy): HTML5 Based SSHv2 Web Client with E2E encryption utilising xterm.js, SJCL & websockets.
|
||||||
- [**JupyterLab**](https://github.com/jupyterlab/jupyterlab): An extensible
|
- [**JupyterLab**](https://github.com/jupyterlab/jupyterlab): An extensible computational environment for Jupyter, supporting interactive data science and scientific computing across all programming languages.
|
||||||
computational environment for Jupyter, supporting interactive data science and scientific computing across all programming languages.
|
|
||||||
- [**Theia**](https://github.com/theia-ide/theia): Theia is a cloud & desktop IDE framework implemented in TypeScript.
|
- [**Theia**](https://github.com/theia-ide/theia): Theia is a cloud & desktop IDE framework implemented in TypeScript.
|
||||||
- [**Opshell**](https://github.com/ricktbaker/opshell) Ops Helper tool to make life easier working with AWS instances across multiple organizations.
|
- [**Opshell**](https://github.com/ricktbaker/opshell) Ops Helper tool to make life easier working with AWS instances across multiple organizations.
|
||||||
- [**Proxmox VE**](https://www.proxmox.com/en/proxmox-ve): Proxmox VE is a complete open-source platform for enterprise virtualization. It uses xterm.js for container terminals and the host shell.
|
- [**Proxmox VE**](https://www.proxmox.com/en/proxmox-ve): Proxmox VE is a complete open-source platform for enterprise virtualization. It uses xterm.js for container terminals and the host shell.
|
||||||
@@ -138,22 +137,23 @@ computational environment for Jupyter, supporting interactive data science and s
|
|||||||
- [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure.
|
- [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure.
|
||||||
- [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace.
|
- [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace.
|
||||||
- [**rtty**](https://github.com/zhaojh329/rtty): A reverse proxy WebTTY. It is composed of the client and the server.
|
- [**rtty**](https://github.com/zhaojh329/rtty): A reverse proxy WebTTY. It is composed of the client and the server.
|
||||||
- [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS
|
- [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS.
|
||||||
- [**abstruse**](https://github.com/bleenco/abstruse): Abstruse CI is a continuous integration platform based on Node.JS and Docker.
|
- [**abstruse**](https://github.com/bleenco/abstruse): Abstruse CI is a continuous integration platform based on Node.JS and Docker.
|
||||||
- [**Azure Data Studio**](https://github.com/Microsoft/azuredatastudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
|
- [**Azure Data Studio**](https://github.com/Microsoft/azuredatastudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
|
||||||
- [**FreeMAN**](https://github.com/matthew-matvei/freeman): A free, cross-platform file manager for power users
|
- [**FreeMAN**](https://github.com/matthew-matvei/freeman): A free, cross-platform file manager for power users.
|
||||||
- [**Fluent Terminal**](https://github.com/felixse/FluentTerminal): A terminal emulator based on UWP and web technologies.
|
- [**Fluent Terminal**](https://github.com/felixse/FluentTerminal): A terminal emulator based on UWP and web technologies.
|
||||||
- [**Hyper**](https://hyper.is): A terminal built on web technologies
|
- [**Hyper**](https://hyper.is): A terminal built on web technologies.
|
||||||
- [**Diag**](https://diag.ai): A better way to troubleshoot problems faster. Capture, share and reapply troubleshooting knowledge so you can focus on solving problems that matter.
|
- [**Diag**](https://diag.ai): A better way to troubleshoot problems faster. Capture, share and reapply troubleshooting knowledge so you can focus on solving problems that matter.
|
||||||
- [**GoTTY**](https://github.com/yudai/gotty): A simple command line tool that shares your terminal as a web application based on xterm.js.
|
- [**GoTTY**](https://github.com/yudai/gotty): A simple command line tool that shares your terminal as a web application based on xterm.js.
|
||||||
- [**genact**](https://github.com/svenstaro/genact): A nonsense activity generator.
|
- [**genact**](https://github.com/svenstaro/genact): A nonsense activity generator.
|
||||||
- [**cPanel & WHM**](https://cpanel.com): The hosting platform of choice.
|
- [**cPanel & WHM**](https://cpanel.com): The hosting platform of choice.
|
||||||
- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to xterm.js
|
- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to xterm.js.
|
||||||
- [**SSH Web Client**](https://github.com/roke22/PHP-SSH2-Web-Client): SSH Web Client with PHP.
|
- [**SSH Web Client**](https://github.com/roke22/PHP-SSH2-Web-Client): SSH Web Client with PHP.
|
||||||
- [**Shellvault**](https://www.shellvault.io): The cloud-based SSH terminal you can access from anywhere.
|
- [**Shellvault**](https://www.shellvault.io): The cloud-based SSH terminal you can access from anywhere.
|
||||||
- [**Juno**](http://junolab.org/): A flexible Julia IDE, based on Atom.
|
- [**Juno**](http://junolab.org/): A flexible Julia IDE, based on Atom.
|
||||||
- [**webssh**](https://github.com/huashengdun/webssh): Web based ssh client.
|
- [**webssh**](https://github.com/huashengdun/webssh): Web based ssh client.
|
||||||
- [**info-beamer hosted**](https://info-beamer.com): Uses Xterm.js to manage digital signage devices from the web dashboard.
|
- [**info-beamer hosted**](https://info-beamer.com): Uses xterm.js to manage digital signage devices from the web dashboard.
|
||||||
|
- [**Jumpserver**](https://github.com/jumpserver/luna): Jumpserver Luna project, Jumpserver is a bastion server project, Luna use xterm.js for web terminal emulation.
|
||||||
|
|
||||||
[And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
|
[And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ Do you use xterm.js in your application as well? Please [open a Pull Request](ht
|
|||||||
|
|
||||||
Xterm.js follows a monthly release cycle roughly.
|
Xterm.js follows a monthly release cycle roughly.
|
||||||
|
|
||||||
All current and past releases are available on this repo's [Releases page](https://github.com/sourcelair/xterm.js/releases), while a rough roadmap is available by looking through [Milestones](https://github.com/sourcelair/xterm.js/milestones).
|
All current and past releases are available on this repo's [Releases page](https://github.com/sourcelair/xterm.js/releases), you can view the [high-level roadmap on the wiki](https://github.com/xtermjs/xterm.js/wiki/Roadmap) and see what we're working on now by looking through [Milestones](https://github.com/sourcelair/xterm.js/milestones).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
* This file is the entry point for browserify.
|
* This file is the entry point for browserify.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const cp = require('child_process');
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const startServer = require('./server.js');
|
const startServer = require('./server.js');
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm",
|
"name": "xterm",
|
||||||
"description": "Full xterm terminal, in your browser",
|
"description": "Full xterm terminal, in your browser",
|
||||||
"version": "3.10.0",
|
"version": "3.11.0",
|
||||||
"main": "lib/public/Terminal.js",
|
"main": "lib/public/Terminal.js",
|
||||||
"types": "typings/xterm.d.ts",
|
"types": "typings/xterm.d.ts",
|
||||||
"repository": "https://github.com/xtermjs/xterm.js",
|
"repository": "https://github.com/xtermjs/xterm.js",
|
||||||
|
|||||||
+51
-32
@@ -19,8 +19,8 @@ describe('Buffer', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
terminal = new MockTerminal();
|
terminal = new MockTerminal();
|
||||||
terminal.cols = INIT_COLS;
|
(terminal as any).cols = INIT_COLS;
|
||||||
terminal.rows = INIT_ROWS;
|
(terminal as any).rows = INIT_ROWS;
|
||||||
terminal.options.scrollback = 1000;
|
terminal.options.scrollback = 1000;
|
||||||
buffer = new Buffer(terminal, true);
|
buffer = new Buffer(terminal, true);
|
||||||
});
|
});
|
||||||
@@ -265,6 +265,7 @@ describe('Buffer', () => {
|
|||||||
const char = String.fromCharCode(code);
|
const char = String.fromCharCode(code);
|
||||||
firstLine.set(i, [null, char, 1, code]);
|
firstLine.set(i, [null, char, 1, code]);
|
||||||
}
|
}
|
||||||
|
buffer.y = 1;
|
||||||
assert.equal(buffer.lines.get(0).length, 5);
|
assert.equal(buffer.lines.get(0).length, 5);
|
||||||
assert.equal(buffer.lines.get(0).translateToString(), 'abcde');
|
assert.equal(buffer.lines.get(0).translateToString(), 'abcde');
|
||||||
buffer.resize(1, 10);
|
buffer.resize(1, 10);
|
||||||
@@ -296,7 +297,7 @@ describe('Buffer', () => {
|
|||||||
buffer.fillViewportRows();
|
buffer.fillViewportRows();
|
||||||
terminal.options.scrollback = 1;
|
terminal.options.scrollback = 1;
|
||||||
buffer.resize(10, 5);
|
buffer.resize(10, 5);
|
||||||
const lastLine = buffer.lines.get(4);
|
const lastLine = buffer.lines.get(3);
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
const code = 'a'.charCodeAt(0) + i;
|
const code = 'a'.charCodeAt(0) + i;
|
||||||
const char = String.fromCharCode(code);
|
const char = String.fromCharCode(code);
|
||||||
@@ -308,27 +309,27 @@ describe('Buffer', () => {
|
|||||||
assert.equal(buffer.y, 4);
|
assert.equal(buffer.y, 4);
|
||||||
assert.equal(buffer.ybase, 1);
|
assert.equal(buffer.ybase, 1);
|
||||||
assert.equal(buffer.lines.length, 6);
|
assert.equal(buffer.lines.length, 6);
|
||||||
assert.equal(buffer.lines.get(0).translateToString(), ' ');
|
assert.equal(buffer.lines.get(0).translateToString(), 'ab');
|
||||||
assert.equal(buffer.lines.get(1).translateToString(), 'ab');
|
assert.equal(buffer.lines.get(1).translateToString(), 'cd');
|
||||||
assert.equal(buffer.lines.get(2).translateToString(), 'cd');
|
assert.equal(buffer.lines.get(2).translateToString(), 'ef');
|
||||||
assert.equal(buffer.lines.get(3).translateToString(), 'ef');
|
assert.equal(buffer.lines.get(3).translateToString(), 'gh');
|
||||||
assert.equal(buffer.lines.get(4).translateToString(), 'gh');
|
assert.equal(buffer.lines.get(4).translateToString(), 'ij');
|
||||||
assert.equal(buffer.lines.get(5).translateToString(), 'ij');
|
assert.equal(buffer.lines.get(5).translateToString(), ' ');
|
||||||
buffer.resize(1, 5);
|
buffer.resize(1, 5);
|
||||||
assert.equal(buffer.y, 4);
|
assert.equal(buffer.y, 4);
|
||||||
assert.equal(buffer.ybase, 1);
|
assert.equal(buffer.ybase, 1);
|
||||||
assert.equal(buffer.lines.length, 6);
|
assert.equal(buffer.lines.length, 6);
|
||||||
assert.equal(buffer.lines.get(0).translateToString(), 'e');
|
assert.equal(buffer.lines.get(0).translateToString(), 'f');
|
||||||
assert.equal(buffer.lines.get(1).translateToString(), 'f');
|
assert.equal(buffer.lines.get(1).translateToString(), 'g');
|
||||||
assert.equal(buffer.lines.get(2).translateToString(), 'g');
|
assert.equal(buffer.lines.get(2).translateToString(), 'h');
|
||||||
assert.equal(buffer.lines.get(3).translateToString(), 'h');
|
assert.equal(buffer.lines.get(3).translateToString(), 'i');
|
||||||
assert.equal(buffer.lines.get(4).translateToString(), 'i');
|
assert.equal(buffer.lines.get(4).translateToString(), 'j');
|
||||||
assert.equal(buffer.lines.get(5).translateToString(), 'j');
|
assert.equal(buffer.lines.get(5).translateToString(), ' ');
|
||||||
buffer.resize(10, 5);
|
buffer.resize(10, 5);
|
||||||
assert.equal(buffer.y, 0);
|
assert.equal(buffer.y, 1);
|
||||||
assert.equal(buffer.ybase, 0);
|
assert.equal(buffer.ybase, 0);
|
||||||
assert.equal(buffer.lines.length, 5);
|
assert.equal(buffer.lines.length, 5);
|
||||||
assert.equal(buffer.lines.get(0).translateToString(), 'efghij ');
|
assert.equal(buffer.lines.get(0).translateToString(), 'fghij ');
|
||||||
assert.equal(buffer.lines.get(1).translateToString(), ' ');
|
assert.equal(buffer.lines.get(1).translateToString(), ' ');
|
||||||
assert.equal(buffer.lines.get(2).translateToString(), ' ');
|
assert.equal(buffer.lines.get(2).translateToString(), ' ');
|
||||||
assert.equal(buffer.lines.get(3).translateToString(), ' ');
|
assert.equal(buffer.lines.get(3).translateToString(), ' ');
|
||||||
@@ -339,6 +340,7 @@ describe('Buffer', () => {
|
|||||||
// 3+ lines removed on a reflow actually remove the right lines
|
// 3+ lines removed on a reflow actually remove the right lines
|
||||||
buffer.fillViewportRows();
|
buffer.fillViewportRows();
|
||||||
buffer.resize(10, 10);
|
buffer.resize(10, 10);
|
||||||
|
buffer.y = 2;
|
||||||
const firstLine = buffer.lines.get(0);
|
const firstLine = buffer.lines.get(0);
|
||||||
const secondLine = buffer.lines.get(1);
|
const secondLine = buffer.lines.get(1);
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
@@ -358,8 +360,8 @@ describe('Buffer', () => {
|
|||||||
assert.equal(buffer.lines.get(i).translateToString(), ' ');
|
assert.equal(buffer.lines.get(i).translateToString(), ' ');
|
||||||
}
|
}
|
||||||
buffer.resize(2, 10);
|
buffer.resize(2, 10);
|
||||||
assert.equal(buffer.ybase, 0);
|
assert.equal(buffer.ybase, 1);
|
||||||
assert.equal(buffer.lines.length, 10);
|
assert.equal(buffer.lines.length, 11);
|
||||||
assert.equal(buffer.lines.get(0).translateToString(), 'ab');
|
assert.equal(buffer.lines.get(0).translateToString(), 'ab');
|
||||||
assert.equal(buffer.lines.get(1).translateToString(), 'cd');
|
assert.equal(buffer.lines.get(1).translateToString(), 'cd');
|
||||||
assert.equal(buffer.lines.get(2).translateToString(), 'ef');
|
assert.equal(buffer.lines.get(2).translateToString(), 'ef');
|
||||||
@@ -370,7 +372,10 @@ describe('Buffer', () => {
|
|||||||
assert.equal(buffer.lines.get(7).translateToString(), '45');
|
assert.equal(buffer.lines.get(7).translateToString(), '45');
|
||||||
assert.equal(buffer.lines.get(8).translateToString(), '67');
|
assert.equal(buffer.lines.get(8).translateToString(), '67');
|
||||||
assert.equal(buffer.lines.get(9).translateToString(), '89');
|
assert.equal(buffer.lines.get(9).translateToString(), '89');
|
||||||
|
assert.equal(buffer.lines.get(10).translateToString(), ' ');
|
||||||
buffer.resize(10, 10);
|
buffer.resize(10, 10);
|
||||||
|
assert.equal(buffer.ybase, 0);
|
||||||
|
assert.equal(buffer.lines.length, 10);
|
||||||
assert.equal(buffer.lines.get(0).translateToString(), 'abcdefghij');
|
assert.equal(buffer.lines.get(0).translateToString(), 'abcdefghij');
|
||||||
assert.equal(buffer.lines.get(1).translateToString(), '0123456789');
|
assert.equal(buffer.lines.get(1).translateToString(), '0123456789');
|
||||||
for (let i = 2; i < 10; i++) {
|
for (let i = 2; i < 10; i++) {
|
||||||
@@ -379,22 +384,23 @@ describe('Buffer', () => {
|
|||||||
});
|
});
|
||||||
it('should transfer combined char data over to reflowed lines', () => {
|
it('should transfer combined char data over to reflowed lines', () => {
|
||||||
buffer.fillViewportRows();
|
buffer.fillViewportRows();
|
||||||
buffer.resize(4, 2);
|
buffer.resize(4, 3);
|
||||||
|
buffer.y = 2;
|
||||||
const firstLine = buffer.lines.get(0);
|
const firstLine = buffer.lines.get(0);
|
||||||
firstLine.set(0, [ null, 'a', 1, 'a'.charCodeAt(0) ]);
|
firstLine.set(0, [ null, 'a', 1, 'a'.charCodeAt(0) ]);
|
||||||
firstLine.set(1, [ null, 'b', 1, 'b'.charCodeAt(0) ]);
|
firstLine.set(1, [ null, 'b', 1, 'b'.charCodeAt(0) ]);
|
||||||
firstLine.set(2, [ null, 'c', 1, 'c'.charCodeAt(0) ]);
|
firstLine.set(2, [ null, 'c', 1, 'c'.charCodeAt(0) ]);
|
||||||
firstLine.set(3, [ null, '😁', 1, '😁'.charCodeAt(0) ]);
|
firstLine.set(3, [ null, '😁', 1, '😁'.charCodeAt(0) ]);
|
||||||
assert.equal(buffer.lines.length, 2);
|
assert.equal(buffer.lines.length, 3);
|
||||||
assert.equal(buffer.lines.get(0).translateToString(), 'abc😁');
|
assert.equal(buffer.lines.get(0).translateToString(), 'abc😁');
|
||||||
assert.equal(buffer.lines.get(1).translateToString(), ' ');
|
assert.equal(buffer.lines.get(1).translateToString(), ' ');
|
||||||
buffer.resize(2, 2);
|
buffer.resize(2, 3);
|
||||||
assert.equal(buffer.lines.get(0).translateToString(), 'ab');
|
assert.equal(buffer.lines.get(0).translateToString(), 'ab');
|
||||||
assert.equal(buffer.lines.get(1).translateToString(), 'c😁');
|
assert.equal(buffer.lines.get(1).translateToString(), 'c😁');
|
||||||
});
|
});
|
||||||
it('should adjust markers when reflowing', () => {
|
it('should adjust markers when reflowing', () => {
|
||||||
buffer.fillViewportRows();
|
buffer.fillViewportRows();
|
||||||
buffer.resize(10, 15);
|
buffer.resize(10, 16);
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
const code = 'a'.charCodeAt(0) + i;
|
const code = 'a'.charCodeAt(0) + i;
|
||||||
const char = String.fromCharCode(code);
|
const char = String.fromCharCode(code);
|
||||||
@@ -410,6 +416,7 @@ describe('Buffer', () => {
|
|||||||
const char = String.fromCharCode(code);
|
const char = String.fromCharCode(code);
|
||||||
buffer.lines.get(2).set(i, [null, char, 1, code]);
|
buffer.lines.get(2).set(i, [null, char, 1, code]);
|
||||||
}
|
}
|
||||||
|
buffer.y = 3;
|
||||||
// Buffer:
|
// Buffer:
|
||||||
// abcdefghij
|
// abcdefghij
|
||||||
// 0123456789
|
// 0123456789
|
||||||
@@ -423,7 +430,7 @@ describe('Buffer', () => {
|
|||||||
assert.equal(firstMarker.line, 0);
|
assert.equal(firstMarker.line, 0);
|
||||||
assert.equal(secondMarker.line, 1);
|
assert.equal(secondMarker.line, 1);
|
||||||
assert.equal(thirdMarker.line, 2);
|
assert.equal(thirdMarker.line, 2);
|
||||||
buffer.resize(2, 15);
|
buffer.resize(2, 16);
|
||||||
assert.equal(buffer.lines.get(0).translateToString(), 'ab');
|
assert.equal(buffer.lines.get(0).translateToString(), 'ab');
|
||||||
assert.equal(buffer.lines.get(1).translateToString(), 'cd');
|
assert.equal(buffer.lines.get(1).translateToString(), 'cd');
|
||||||
assert.equal(buffer.lines.get(2).translateToString(), 'ef');
|
assert.equal(buffer.lines.get(2).translateToString(), 'ef');
|
||||||
@@ -442,7 +449,7 @@ describe('Buffer', () => {
|
|||||||
assert.equal(firstMarker.line, 0, 'first marker should remain unchanged');
|
assert.equal(firstMarker.line, 0, 'first marker should remain unchanged');
|
||||||
assert.equal(secondMarker.line, 5, 'second marker should be shifted since the first line wrapped');
|
assert.equal(secondMarker.line, 5, 'second marker should be shifted since the first line wrapped');
|
||||||
assert.equal(thirdMarker.line, 10, 'third marker should be shifted since the first and second lines wrapped');
|
assert.equal(thirdMarker.line, 10, 'third marker should be shifted since the first and second lines wrapped');
|
||||||
buffer.resize(10, 15);
|
buffer.resize(10, 16);
|
||||||
assert.equal(buffer.lines.get(0).translateToString(), 'abcdefghij');
|
assert.equal(buffer.lines.get(0).translateToString(), 'abcdefghij');
|
||||||
assert.equal(buffer.lines.get(1).translateToString(), '0123456789');
|
assert.equal(buffer.lines.get(1).translateToString(), '0123456789');
|
||||||
assert.equal(buffer.lines.get(2).translateToString(), 'klmnopqrst');
|
assert.equal(buffer.lines.get(2).translateToString(), 'klmnopqrst');
|
||||||
@@ -456,7 +463,7 @@ describe('Buffer', () => {
|
|||||||
it('should dispose markers whose rows are trimmed during a reflow', () => {
|
it('should dispose markers whose rows are trimmed during a reflow', () => {
|
||||||
buffer.fillViewportRows();
|
buffer.fillViewportRows();
|
||||||
terminal.options.scrollback = 1;
|
terminal.options.scrollback = 1;
|
||||||
buffer.resize(10, 10);
|
buffer.resize(10, 11);
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
const code = 'a'.charCodeAt(0) + i;
|
const code = 'a'.charCodeAt(0) + i;
|
||||||
const char = String.fromCharCode(code);
|
const char = String.fromCharCode(code);
|
||||||
@@ -472,6 +479,7 @@ describe('Buffer', () => {
|
|||||||
const char = String.fromCharCode(code);
|
const char = String.fromCharCode(code);
|
||||||
buffer.lines.get(2).set(i, [null, char, 1, code]);
|
buffer.lines.get(2).set(i, [null, char, 1, code]);
|
||||||
}
|
}
|
||||||
|
buffer.y = 10;
|
||||||
// Buffer:
|
// Buffer:
|
||||||
// abcdefghij
|
// abcdefghij
|
||||||
// 0123456789
|
// 0123456789
|
||||||
@@ -479,14 +487,14 @@ describe('Buffer', () => {
|
|||||||
const firstMarker = buffer.addMarker(0);
|
const firstMarker = buffer.addMarker(0);
|
||||||
const secondMarker = buffer.addMarker(1);
|
const secondMarker = buffer.addMarker(1);
|
||||||
const thirdMarker = buffer.addMarker(2);
|
const thirdMarker = buffer.addMarker(2);
|
||||||
buffer.y = 2;
|
buffer.y = 3;
|
||||||
assert.equal(buffer.lines.get(0).translateToString(), 'abcdefghij');
|
assert.equal(buffer.lines.get(0).translateToString(), 'abcdefghij');
|
||||||
assert.equal(buffer.lines.get(1).translateToString(), '0123456789');
|
assert.equal(buffer.lines.get(1).translateToString(), '0123456789');
|
||||||
assert.equal(buffer.lines.get(2).translateToString(), 'klmnopqrst');
|
assert.equal(buffer.lines.get(2).translateToString(), 'klmnopqrst');
|
||||||
assert.equal(firstMarker.line, 0);
|
assert.equal(firstMarker.line, 0);
|
||||||
assert.equal(secondMarker.line, 1);
|
assert.equal(secondMarker.line, 1);
|
||||||
assert.equal(thirdMarker.line, 2);
|
assert.equal(thirdMarker.line, 2);
|
||||||
buffer.resize(2, 10);
|
buffer.resize(2, 11);
|
||||||
assert.equal(buffer.lines.get(0).translateToString(), 'ij');
|
assert.equal(buffer.lines.get(0).translateToString(), 'ij');
|
||||||
assert.equal(buffer.lines.get(1).translateToString(), '01');
|
assert.equal(buffer.lines.get(1).translateToString(), '01');
|
||||||
assert.equal(buffer.lines.get(2).translateToString(), '23');
|
assert.equal(buffer.lines.get(2).translateToString(), '23');
|
||||||
@@ -503,7 +511,7 @@ describe('Buffer', () => {
|
|||||||
assert.equal(firstMarker.isDisposed, true, 'first marker was trimmed');
|
assert.equal(firstMarker.isDisposed, true, 'first marker was trimmed');
|
||||||
assert.equal(secondMarker.isDisposed, false);
|
assert.equal(secondMarker.isDisposed, false);
|
||||||
assert.equal(thirdMarker.isDisposed, false);
|
assert.equal(thirdMarker.isDisposed, false);
|
||||||
buffer.resize(10, 10);
|
buffer.resize(10, 11);
|
||||||
assert.equal(buffer.lines.get(0).translateToString(), 'ij ');
|
assert.equal(buffer.lines.get(0).translateToString(), 'ij ');
|
||||||
assert.equal(buffer.lines.get(1).translateToString(), '0123456789');
|
assert.equal(buffer.lines.get(1).translateToString(), '0123456789');
|
||||||
assert.equal(buffer.lines.get(2).translateToString(), 'klmnopqrst');
|
assert.equal(buffer.lines.get(2).translateToString(), 'klmnopqrst');
|
||||||
@@ -513,6 +521,7 @@ describe('Buffer', () => {
|
|||||||
it('should wrap wide characters correctly when reflowing larger', () => {
|
it('should wrap wide characters correctly when reflowing larger', () => {
|
||||||
buffer.fillViewportRows();
|
buffer.fillViewportRows();
|
||||||
buffer.resize(12, 10);
|
buffer.resize(12, 10);
|
||||||
|
buffer.y = 2;
|
||||||
for (let i = 0; i < 12; i += 4) {
|
for (let i = 0; i < 12; i += 4) {
|
||||||
buffer.lines.get(0).set(i, [null, '汉', 2, '汉'.charCodeAt(0)]);
|
buffer.lines.get(0).set(i, [null, '汉', 2, '汉'.charCodeAt(0)]);
|
||||||
buffer.lines.get(1).set(i, [null, '汉', 2, '汉'.charCodeAt(0)]);
|
buffer.lines.get(1).set(i, [null, '汉', 2, '汉'.charCodeAt(0)]);
|
||||||
@@ -547,6 +556,7 @@ describe('Buffer', () => {
|
|||||||
it('should wrap wide characters correctly when reflowing smaller', () => {
|
it('should wrap wide characters correctly when reflowing smaller', () => {
|
||||||
buffer.fillViewportRows();
|
buffer.fillViewportRows();
|
||||||
buffer.resize(12, 10);
|
buffer.resize(12, 10);
|
||||||
|
buffer.y = 2;
|
||||||
for (let i = 0; i < 12; i += 4) {
|
for (let i = 0; i < 12; i += 4) {
|
||||||
buffer.lines.get(0).set(i, [null, '汉', 2, '汉'.charCodeAt(0)]);
|
buffer.lines.get(0).set(i, [null, '汉', 2, '汉'.charCodeAt(0)]);
|
||||||
buffer.lines.get(1).set(i, [null, '汉', 2, '汉'.charCodeAt(0)]);
|
buffer.lines.get(1).set(i, [null, '汉', 2, '汉'.charCodeAt(0)]);
|
||||||
@@ -937,6 +947,7 @@ describe('Buffer', () => {
|
|||||||
describe('&& ydisp === ybase', () => {
|
describe('&& ydisp === ybase', () => {
|
||||||
it('should trim lines and keep ydisp = ybase', () => {
|
it('should trim lines and keep ydisp = ybase', () => {
|
||||||
buffer.ydisp = 10;
|
buffer.ydisp = 10;
|
||||||
|
buffer.y = 13;
|
||||||
buffer.resize(2, 10);
|
buffer.resize(2, 10);
|
||||||
assert.equal(buffer.ydisp, 10);
|
assert.equal(buffer.ydisp, 10);
|
||||||
assert.equal(buffer.ybase, 10);
|
assert.equal(buffer.ybase, 10);
|
||||||
@@ -962,6 +973,7 @@ describe('Buffer', () => {
|
|||||||
describe('&& ydisp !== ybase', () => {
|
describe('&& ydisp !== ybase', () => {
|
||||||
it('should trim lines and not change ydisp', () => {
|
it('should trim lines and not change ydisp', () => {
|
||||||
buffer.ydisp = 5;
|
buffer.ydisp = 5;
|
||||||
|
buffer.y = 13;
|
||||||
buffer.resize(2, 10);
|
buffer.resize(2, 10);
|
||||||
assert.equal(buffer.ydisp, 5);
|
assert.equal(buffer.ydisp, 5);
|
||||||
assert.equal(buffer.ybase, 10);
|
assert.equal(buffer.ybase, 10);
|
||||||
@@ -1266,7 +1278,7 @@ describe('Buffer', () => {
|
|||||||
const s = terminal.buffer.iterator(true).next().content;
|
const s = terminal.buffer.iterator(true).next().content;
|
||||||
assert.equal(input, s);
|
assert.equal(input, s);
|
||||||
for (let i = 10; i < input.length; ++i) {
|
for (let i = 10; i < input.length; ++i) {
|
||||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||||
const j = (i - 0) << 1;
|
const j = (i - 0) << 1;
|
||||||
assert.deepEqual([(j / terminal.cols) | 0, j % terminal.cols], bufferIndex);
|
assert.deepEqual([(j / terminal.cols) | 0, j % terminal.cols], bufferIndex);
|
||||||
}
|
}
|
||||||
@@ -1278,7 +1290,7 @@ describe('Buffer', () => {
|
|||||||
const s = terminal.buffer.iterator(true).next().content;
|
const s = terminal.buffer.iterator(true).next().content;
|
||||||
assert.equal(input, s);
|
assert.equal(input, s);
|
||||||
for (let i = 0; i < input.length; ++i) {
|
for (let i = 0; i < input.length; ++i) {
|
||||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||||
assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0]).get(bufferIndex[1])[CHAR_DATA_CHAR_INDEX]);
|
assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0]).get(bufferIndex[1])[CHAR_DATA_CHAR_INDEX]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1290,7 +1302,7 @@ describe('Buffer', () => {
|
|||||||
const s = terminal.buffer.iterator(true).next().content;
|
const s = terminal.buffer.iterator(true).next().content;
|
||||||
assert.equal(input, s);
|
assert.equal(input, s);
|
||||||
for (let i = 0; i < input.length; ++i) {
|
for (let i = 0; i < input.length; ++i) {
|
||||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
(!(i % 3))
|
(!(i % 3))
|
||||||
? input[i]
|
? input[i]
|
||||||
@@ -1300,6 +1312,13 @@ describe('Buffer', () => {
|
|||||||
terminal.buffer.lines.get(bufferIndex[0]).get(bufferIndex[1])[CHAR_DATA_CHAR_INDEX]);
|
terminal.buffer.lines.get(bufferIndex[0]).get(bufferIndex[1])[CHAR_DATA_CHAR_INDEX]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle \t in lines correctly', () => {
|
||||||
|
const input = '\thttps://google.de';
|
||||||
|
terminal.writeSync(input);
|
||||||
|
const s = terminal.buffer.iterator(true).next().content;
|
||||||
|
assert.equal(s, Array(terminal.getOption('tabStopWidth') + 1).join(' ') + 'https://google.de');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
describe('BufferStringIterator', function(): void {
|
describe('BufferStringIterator', function(): void {
|
||||||
it('iterator does not overflow buffer limits', function(): void {
|
it('iterator does not overflow buffer limits', function(): void {
|
||||||
|
|||||||
+42
-23
@@ -3,13 +3,13 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CircularList, IInsertEvent, IDeleteEvent } from './common/CircularList';
|
|
||||||
import { CharData, ITerminal, IBuffer, IBufferLine, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult } from './Types';
|
|
||||||
import { EventEmitter } from './common/EventEmitter';
|
|
||||||
import { IMarker } from 'xterm';
|
import { IMarker } from 'xterm';
|
||||||
import { BufferLine } from './BufferLine';
|
import { BufferLine } from './BufferLine';
|
||||||
|
import { reflowLargerApplyNewLayout, reflowLargerCreateNewLayout, reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths } from './BufferReflow';
|
||||||
|
import { CircularList, IDeleteEvent, IInsertEvent } from './common/CircularList';
|
||||||
|
import { EventEmitter } from './common/EventEmitter';
|
||||||
import { DEFAULT_COLOR } from './renderer/atlas/Types';
|
import { DEFAULT_COLOR } from './renderer/atlas/Types';
|
||||||
import { reflowSmallerGetNewLineLengths, reflowLargerGetLinesToRemove, reflowLargerCreateNewLayout, reflowLargerApplyNewLayout } from './BufferReflow';
|
import { BufferIndex, CharData, IBuffer, IBufferLine, IBufferStringIterator, IBufferStringIteratorResult, ITerminal } from './Types';
|
||||||
|
|
||||||
export const DEFAULT_ATTR = (0 << 18) | (DEFAULT_COLOR << 9) | (256 << 0);
|
export const DEFAULT_ATTR = (0 << 18) | (DEFAULT_COLOR << 9) | (256 << 0);
|
||||||
export const CHAR_DATA_ATTR_INDEX = 0;
|
export const CHAR_DATA_ATTR_INDEX = 0;
|
||||||
@@ -212,7 +212,7 @@ export class Buffer implements IBuffer {
|
|||||||
this.scrollBottom = newRows - 1;
|
this.scrollBottom = newRows - 1;
|
||||||
|
|
||||||
if (this._hasScrollback) {
|
if (this._hasScrollback) {
|
||||||
this._reflow(newCols);
|
this._reflow(newCols, newRows);
|
||||||
|
|
||||||
// Trim the end of the line off if cols shrunk
|
// Trim the end of the line off if cols shrunk
|
||||||
if (this._cols > newCols) {
|
if (this._cols > newCols) {
|
||||||
@@ -226,36 +226,40 @@ export class Buffer implements IBuffer {
|
|||||||
this._rows = newRows;
|
this._rows = newRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _reflow(newCols: number): void {
|
private _reflow(newCols: number, newRows: number): void {
|
||||||
if (this._cols === newCols) {
|
if (this._cols === newCols) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate through rows, ignore the last one as it cannot be wrapped
|
// Iterate through rows, ignore the last one as it cannot be wrapped
|
||||||
if (newCols > this._cols) {
|
if (newCols > this._cols) {
|
||||||
this._reflowLarger(newCols);
|
this._reflowLarger(newCols, newRows);
|
||||||
} else {
|
} else {
|
||||||
this._reflowSmaller(newCols);
|
this._reflowSmaller(newCols, newRows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _reflowLarger(newCols: number): void {
|
private _reflowLarger(newCols: number, newRows: number): void {
|
||||||
const toRemove: number[] = reflowLargerGetLinesToRemove(this.lines, newCols);
|
const toRemove: number[] = reflowLargerGetLinesToRemove(this.lines, newCols, this.ybase + this.y);
|
||||||
if (toRemove.length > 0) {
|
if (toRemove.length > 0) {
|
||||||
const newLayoutResult = reflowLargerCreateNewLayout(this.lines, toRemove);
|
const newLayoutResult = reflowLargerCreateNewLayout(this.lines, toRemove);
|
||||||
reflowLargerApplyNewLayout(this.lines, newLayoutResult.layout);
|
reflowLargerApplyNewLayout(this.lines, newLayoutResult.layout);
|
||||||
this._reflowLargerAdjustViewport(newCols, newLayoutResult.countRemoved);
|
this._reflowLargerAdjustViewport(newCols, newRows, newLayoutResult.countRemoved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _reflowLargerAdjustViewport(newCols: number, countRemoved: number): void {
|
private _reflowLargerAdjustViewport(newCols: number, newRows: number, countRemoved: number): void {
|
||||||
// Adjust viewport based on number of items removed
|
// Adjust viewport based on number of items removed
|
||||||
let viewportAdjustments = countRemoved;
|
let viewportAdjustments = countRemoved;
|
||||||
while (viewportAdjustments-- > 0) {
|
while (viewportAdjustments-- > 0) {
|
||||||
if (this.ybase === 0) {
|
if (this.ybase === 0) {
|
||||||
this.y--;
|
if (this.y > 0) {
|
||||||
// Add an extra row at the bottom of the viewport
|
this.y--;
|
||||||
this.lines.push(new BufferLine(newCols, FILL_CHAR_DATA));
|
}
|
||||||
|
if (this.lines.length < newRows) {
|
||||||
|
// Add an extra row at the bottom of the viewport
|
||||||
|
this.lines.push(new BufferLine(newCols, FILL_CHAR_DATA));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.ydisp === this.ybase) {
|
if (this.ydisp === this.ybase) {
|
||||||
this.ydisp--;
|
this.ydisp--;
|
||||||
@@ -265,7 +269,7 @@ export class Buffer implements IBuffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _reflowSmaller(newCols: number): void {
|
private _reflowSmaller(newCols: number, newRows: number): void {
|
||||||
// Gather all BufferLines that need to be inserted into the Buffer here so that they can be
|
// Gather all BufferLines that need to be inserted into the Buffer here so that they can be
|
||||||
// batched up and only committed once
|
// batched up and only committed once
|
||||||
const toInsert = [];
|
const toInsert = [];
|
||||||
@@ -285,6 +289,13 @@ export class Buffer implements IBuffer {
|
|||||||
wrappedLines.unshift(nextLine);
|
wrappedLines.unshift(nextLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If these lines contain the cursor don't touch them, the program will handle fixing up
|
||||||
|
// wrapped lines with the cursor
|
||||||
|
const absoluteY = this.ybase + this.y;
|
||||||
|
if (absoluteY >= y && absoluteY < y + wrappedLines.length) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const lastLineLength = wrappedLines[wrappedLines.length - 1].getTrimmedLength();
|
const lastLineLength = wrappedLines[wrappedLines.length - 1].getTrimmedLength();
|
||||||
const destLineLengths = reflowSmallerGetNewLineLengths(wrappedLines, this._cols, newCols);
|
const destLineLengths = reflowSmallerGetNewLineLengths(wrappedLines, this._cols, newCols);
|
||||||
const linesToAdd = destLineLengths.length - wrappedLines.length;
|
const linesToAdd = destLineLengths.length - wrappedLines.length;
|
||||||
@@ -349,7 +360,7 @@ export class Buffer implements IBuffer {
|
|||||||
let viewportAdjustments = linesToAdd - trimmedLines;
|
let viewportAdjustments = linesToAdd - trimmedLines;
|
||||||
while (viewportAdjustments-- > 0) {
|
while (viewportAdjustments-- > 0) {
|
||||||
if (this.ybase === 0) {
|
if (this.ybase === 0) {
|
||||||
if (this.y < this._rows - 1) {
|
if (this.y < newRows - 1) {
|
||||||
this.y++;
|
this.y++;
|
||||||
this.lines.pop();
|
this.lines.pop();
|
||||||
} else {
|
} else {
|
||||||
@@ -357,10 +368,13 @@ export class Buffer implements IBuffer {
|
|||||||
this.ydisp++;
|
this.ydisp++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.ybase === this.ydisp) {
|
// Ensure ybase does not exceed its maximum value
|
||||||
this.ydisp++;
|
if (this.ybase < Math.min(this.lines.maxLength, this.lines.length + countToInsert) - newRows) {
|
||||||
|
if (this.ybase === this.ydisp) {
|
||||||
|
this.ydisp++;
|
||||||
|
}
|
||||||
|
this.ybase++;
|
||||||
}
|
}
|
||||||
this.ybase++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,14 +449,19 @@ export class Buffer implements IBuffer {
|
|||||||
* @param stringIndex index within the string
|
* @param stringIndex index within the string
|
||||||
* @param startCol column offset the string was retrieved from
|
* @param startCol column offset the string was retrieved from
|
||||||
*/
|
*/
|
||||||
public stringIndexToBufferIndex(lineIndex: number, stringIndex: number): BufferIndex {
|
public stringIndexToBufferIndex(lineIndex: number, stringIndex: number, trimRight: boolean = false): BufferIndex {
|
||||||
while (stringIndex) {
|
while (stringIndex) {
|
||||||
const line = this.lines.get(lineIndex);
|
const line = this.lines.get(lineIndex);
|
||||||
if (!line) {
|
if (!line) {
|
||||||
return [-1, -1];
|
return [-1, -1];
|
||||||
}
|
}
|
||||||
for (let i = 0; i < line.length; ++i) {
|
const length = (trimRight) ? line.getTrimmedLength() : line.length;
|
||||||
stringIndex -= line.get(i)[CHAR_DATA_CHAR_INDEX].length;
|
for (let i = 0; i < length; ++i) {
|
||||||
|
if (line.get(i)[CHAR_DATA_WIDTH_INDEX]) {
|
||||||
|
// empty cells report a string length of 0, but get replaced
|
||||||
|
// with a whitespace in translateToString, thus replace with 1
|
||||||
|
stringIndex -= line.get(i)[CHAR_DATA_CHAR_INDEX].length || 1;
|
||||||
|
}
|
||||||
if (stringIndex < 0) {
|
if (stringIndex < 0) {
|
||||||
return [lineIndex, i];
|
return [lineIndex, i];
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-4
@@ -3,10 +3,10 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { FILL_CHAR_DATA } from './Buffer';
|
||||||
import { BufferLine } from './BufferLine';
|
import { BufferLine } from './BufferLine';
|
||||||
import { CircularList, IDeleteEvent } from './common/CircularList';
|
import { CircularList, IDeleteEvent } from './common/CircularList';
|
||||||
import { IBufferLine } from './Types';
|
import { IBufferLine } from './Types';
|
||||||
import { FILL_CHAR_DATA } from './Buffer';
|
|
||||||
|
|
||||||
export interface INewLayoutResult {
|
export interface INewLayoutResult {
|
||||||
layout: number[];
|
layout: number[];
|
||||||
@@ -19,7 +19,7 @@ export interface INewLayoutResult {
|
|||||||
* @param lines The buffer lines.
|
* @param lines The buffer lines.
|
||||||
* @param newCols The columns after resize.
|
* @param newCols The columns after resize.
|
||||||
*/
|
*/
|
||||||
export function reflowLargerGetLinesToRemove(lines: CircularList<IBufferLine>, newCols: number): number[] {
|
export function reflowLargerGetLinesToRemove(lines: CircularList<IBufferLine>, newCols: number, bufferAbsoluteY: number): number[] {
|
||||||
// Gather all BufferLines that need to be removed from the Buffer here so that they can be
|
// Gather all BufferLines that need to be removed from the Buffer here so that they can be
|
||||||
// batched up and only committed once
|
// batched up and only committed once
|
||||||
const toRemove: number[] = [];
|
const toRemove: number[] = [];
|
||||||
@@ -39,6 +39,13 @@ export function reflowLargerGetLinesToRemove(lines: CircularList<IBufferLine>, n
|
|||||||
nextLine = lines.get(++i) as BufferLine;
|
nextLine = lines.get(++i) as BufferLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If these lines contain the cursor don't touch them, the program will handle fixing up wrapped
|
||||||
|
// lines with the cursor
|
||||||
|
if (bufferAbsoluteY >= y && bufferAbsoluteY < i) {
|
||||||
|
y += wrappedLines.length - 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Copy buffer data to new locations
|
// Copy buffer data to new locations
|
||||||
let destLineIndex = 0;
|
let destLineIndex = 0;
|
||||||
let destCol = wrappedLines[destLineIndex].getTrimmedLength();
|
let destCol = wrappedLines[destLineIndex].getTrimmedLength();
|
||||||
@@ -64,7 +71,7 @@ export function reflowLargerGetLinesToRemove(lines: CircularList<IBufferLine>, n
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the last cell isn't wide, if it is copy it to the current dest
|
// Make sure the last cell isn't wide, if it is copy it to the current dest
|
||||||
if (destCol === 0) {
|
if (destCol === 0 && destLineIndex !== 0) {
|
||||||
if (wrappedLines[destLineIndex - 1].getWidth(newCols - 1) === 2) {
|
if (wrappedLines[destLineIndex - 1].getWidth(newCols - 1) === 2) {
|
||||||
wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[destLineIndex - 1], newCols - 1, destCol++, 1, false);
|
wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[destLineIndex - 1], newCols - 1, destCol++, 1, false);
|
||||||
// Null out the end of the last row
|
// Null out the end of the last row
|
||||||
@@ -166,7 +173,6 @@ export function reflowLargerApplyNewLayout(lines: CircularList<IBufferLine>, new
|
|||||||
*/
|
*/
|
||||||
export function reflowSmallerGetNewLineLengths(wrappedLines: BufferLine[], oldCols: number, newCols: number): number[] {
|
export function reflowSmallerGetNewLineLengths(wrappedLines: BufferLine[], oldCols: number, newCols: number): number[] {
|
||||||
const newLineLengths: number[] = [];
|
const newLineLengths: number[] = [];
|
||||||
|
|
||||||
const cellsNeeded = wrappedLines.map(l => l.getTrimmedLength()).reduce((p, c) => p + c);
|
const cellsNeeded = wrappedLines.map(l => l.getTrimmedLength()).reduce((p, c) => p + c);
|
||||||
|
|
||||||
// Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and
|
// Use srcCol and srcLine to find the new wrapping point, use that to get the cellsAvailable and
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ describe('BufferSet', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
terminal = new MockTerminal();
|
terminal = new MockTerminal();
|
||||||
terminal.cols = 80;
|
(terminal as any).cols = 80;
|
||||||
terminal.rows = 24;
|
(terminal as any).rows = 24;
|
||||||
terminal.options.scrollback = 1000;
|
terminal.options.scrollback = 1000;
|
||||||
bufferSet = new BufferSet(terminal);
|
bufferSet = new BufferSet(terminal);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -203,6 +203,8 @@ export class CompositionHelper {
|
|||||||
this._compositionView.style.top = cursorTop + 'px';
|
this._compositionView.style.top = cursorTop + 'px';
|
||||||
this._compositionView.style.height = cellHeight + 'px';
|
this._compositionView.style.height = cellHeight + 'px';
|
||||||
this._compositionView.style.lineHeight = cellHeight + 'px';
|
this._compositionView.style.lineHeight = cellHeight + 'px';
|
||||||
|
this._compositionView.style.fontFamily = this._terminal.options.fontFamily;
|
||||||
|
this._compositionView.style.fontSize = this._terminal.options.fontSize + 'px';
|
||||||
// Sync the textarea to the exact position of the composition view so the IME knows where the
|
// Sync the textarea to the exact position of the composition view so the IME knows where the
|
||||||
// text is.
|
// text is.
|
||||||
const compositionViewBounds = this._compositionView.getBoundingClientRect();
|
const compositionViewBounds = this._compositionView.getBoundingClientRect();
|
||||||
|
|||||||
+171
-150
@@ -6,6 +6,7 @@
|
|||||||
import { ParserState, IDcsHandler, IParsingState } from './Types';
|
import { ParserState, IDcsHandler, IParsingState } from './Types';
|
||||||
import { EscapeSequenceParser, TransitionTable, VT500_TRANSITION_TABLE } from './EscapeSequenceParser';
|
import { EscapeSequenceParser, TransitionTable, VT500_TRANSITION_TABLE } from './EscapeSequenceParser';
|
||||||
import * as chai from 'chai';
|
import * as chai from 'chai';
|
||||||
|
import { StringToUtf32, stringFromCodePoint } from './core/input/TextDecoder';
|
||||||
|
|
||||||
function r(a: number, b: number): string[] {
|
function r(a: number, b: number): string[] {
|
||||||
let c = b - a;
|
let c = b - a;
|
||||||
@@ -50,8 +51,12 @@ const testTerminal: any = {
|
|||||||
compare: function (value: any): void {
|
compare: function (value: any): void {
|
||||||
chai.expect(this.calls.slice()).eql(value); // weird bug w'o slicing here
|
chai.expect(this.calls.slice()).eql(value); // weird bug w'o slicing here
|
||||||
},
|
},
|
||||||
print: function (data: string, start: number, end: number): void {
|
print: function (data: Uint32Array, start: number, end: number): void {
|
||||||
this.calls.push(['print', data.substring(start, end)]);
|
let s = '';
|
||||||
|
for (let i = start; i < end; ++i) {
|
||||||
|
s += stringFromCodePoint(data[i]);
|
||||||
|
}
|
||||||
|
this.calls.push(['print', s]);
|
||||||
},
|
},
|
||||||
actionOSC: function (s: string): void {
|
actionOSC: function (s: string): void {
|
||||||
this.calls.push(['osc', s]);
|
this.calls.push(['osc', s]);
|
||||||
@@ -68,8 +73,12 @@ const testTerminal: any = {
|
|||||||
actionDCSHook: function (collect: string, params: number[], flag: string): void {
|
actionDCSHook: function (collect: string, params: number[], flag: string): void {
|
||||||
this.calls.push(['dcs hook', collect, params, flag]);
|
this.calls.push(['dcs hook', collect, params, flag]);
|
||||||
},
|
},
|
||||||
actionDCSPrint: function (data: string, start: number, end: number): void {
|
actionDCSPrint: function (data: Uint32Array, start: number, end: number): void {
|
||||||
this.calls.push(['dcs put', data.substring(start, end)]);
|
let s = '';
|
||||||
|
for (let i = start; i < end; ++i) {
|
||||||
|
s += stringFromCodePoint(data[i]);
|
||||||
|
}
|
||||||
|
this.calls.push(['dcs put', s]);
|
||||||
},
|
},
|
||||||
actionDCSUnhook: function (): void {
|
actionDCSUnhook: function (): void {
|
||||||
this.calls.push(['dcs unhook']);
|
this.calls.push(['dcs unhook']);
|
||||||
@@ -81,7 +90,7 @@ class DcsTest implements IDcsHandler {
|
|||||||
hook(collect: string, params: number[], flag: number): void {
|
hook(collect: string, params: number[], flag: number): void {
|
||||||
testTerminal.actionDCSHook(collect, params, String.fromCharCode(flag));
|
testTerminal.actionDCSHook(collect, params, String.fromCharCode(flag));
|
||||||
}
|
}
|
||||||
put(data: string, start: number, end: number): void {
|
put(data: Uint32Array, start: number, end: number): void {
|
||||||
testTerminal.actionDCSPrint(data, start, end);
|
testTerminal.actionDCSPrint(data, start, end);
|
||||||
}
|
}
|
||||||
unhook(): void {
|
unhook(): void {
|
||||||
@@ -155,6 +164,12 @@ interface IRun {
|
|||||||
parser: TestEscapeSequenceParser;
|
parser: TestEscapeSequenceParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// translate string based parse calls into typed array based
|
||||||
|
function parse(parser: TestEscapeSequenceParser, data: string): void {
|
||||||
|
const container = new Uint32Array(data.length);
|
||||||
|
const decoder = new StringToUtf32();
|
||||||
|
parser.parse(container, decoder.decode(data, container));
|
||||||
|
}
|
||||||
|
|
||||||
describe('EscapeSequenceParser', function (): void {
|
describe('EscapeSequenceParser', function (): void {
|
||||||
let parser: TestEscapeSequenceParser | null = null;
|
let parser: TestEscapeSequenceParser | null = null;
|
||||||
@@ -205,12 +220,12 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('state GROUND execute action', function (): void {
|
it('state GROUND execute action', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
const exes = r(0x00, 0x18);
|
let exes = r(0x00, 0x18);
|
||||||
exes.concat(['\x19']);
|
exes = exes.concat(['\x19']);
|
||||||
exes.concat(r(0x1c, 0x20));
|
exes = exes.concat(r(0x1c, 0x20));
|
||||||
for (let i = 0; i < exes.length; ++i) {
|
for (let i = 0; i < exes.length; ++i) {
|
||||||
parser.currentState = ParserState.GROUND;
|
parser.currentState = ParserState.GROUND;
|
||||||
parser.parse(exes[i]);
|
parse(parser, exes[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
||||||
testTerminal.compare([['exe', exes[i]]]);
|
testTerminal.compare([['exe', exes[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -223,7 +238,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const printables = r(0x20, 0x7f); // NOTE: DEL excluded
|
const printables = r(0x20, 0x7f); // NOTE: DEL excluded
|
||||||
for (let i = 0; i < printables.length; ++i) {
|
for (let i = 0; i < printables.length; ++i) {
|
||||||
parser.currentState = ParserState.GROUND;
|
parser.currentState = ParserState.GROUND;
|
||||||
parser.parse(printables[i]);
|
parse(parser, printables[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
||||||
testTerminal.compare([['print', printables[i]]]);
|
testTerminal.compare([['print', printables[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -245,13 +260,13 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
for (state in states) {
|
for (state in states) {
|
||||||
for (let i = 0; i < exes.length; ++i) {
|
for (let i = 0; i < exes.length; ++i) {
|
||||||
parser.currentState = state;
|
parser.currentState = state;
|
||||||
parser.parse(exes[i]);
|
parse(parser, exes[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
||||||
testTerminal.compare((state in exceptions ? exceptions[state][exes[i]] : 0) || [['exe', exes[i]]]);
|
testTerminal.compare((state in exceptions ? exceptions[state][exes[i]] : 0) || [['exe', exes[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
}
|
}
|
||||||
parser.parse('\x9c');
|
parse(parser, '\x9c');
|
||||||
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
||||||
testTerminal.compare([]);
|
testTerminal.compare([]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -265,7 +280,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.osc = '#';
|
parser.osc = '#';
|
||||||
parser.params = [23];
|
parser.params = [23];
|
||||||
parser.collect = '#';
|
parser.collect = '#';
|
||||||
parser.parse('\x1b');
|
parse(parser, '\x1b');
|
||||||
chai.expect(parser.currentState).equal(ParserState.ESCAPE);
|
chai.expect(parser.currentState).equal(ParserState.ESCAPE);
|
||||||
chai.expect(parser.osc).equal('');
|
chai.expect(parser.osc).equal('');
|
||||||
chai.expect(parser.params).eql([0]);
|
chai.expect(parser.params).eql([0]);
|
||||||
@@ -276,12 +291,12 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('state ESCAPE execute rules', function (): void {
|
it('state ESCAPE execute rules', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
const exes = r(0x00, 0x18);
|
let exes = r(0x00, 0x18);
|
||||||
exes.concat(['\x19']);
|
exes = exes.concat(['\x19']);
|
||||||
exes.concat(r(0x1c, 0x20));
|
exes = exes.concat(r(0x1c, 0x20));
|
||||||
for (let i = 0; i < exes.length; ++i) {
|
for (let i = 0; i < exes.length; ++i) {
|
||||||
parser.currentState = ParserState.ESCAPE;
|
parser.currentState = ParserState.ESCAPE;
|
||||||
parser.parse(exes[i]);
|
parse(parser, exes[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.ESCAPE);
|
chai.expect(parser.currentState).equal(ParserState.ESCAPE);
|
||||||
testTerminal.compare([['exe', exes[i]]]);
|
testTerminal.compare([['exe', exes[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -292,7 +307,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
parser.currentState = ParserState.ESCAPE;
|
parser.currentState = ParserState.ESCAPE;
|
||||||
parser.parse('\x7f');
|
parse(parser, '\x7f');
|
||||||
chai.expect(parser.currentState).equal(ParserState.ESCAPE);
|
chai.expect(parser.currentState).equal(ParserState.ESCAPE);
|
||||||
testTerminal.compare([]);
|
testTerminal.compare([]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -301,13 +316,13 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('trans ESCAPE --> GROUND with ecs_dispatch action', function (): void {
|
it('trans ESCAPE --> GROUND with ecs_dispatch action', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
const dispatches = r(0x30, 0x50);
|
let dispatches = r(0x30, 0x50);
|
||||||
dispatches.concat(r(0x51, 0x58));
|
dispatches = dispatches.concat(r(0x51, 0x58));
|
||||||
dispatches.concat(['\x59', '\x5a', '\x5c']);
|
dispatches = dispatches.concat(['\x59', '\x5a']); // excluded \x5c
|
||||||
dispatches.concat(r(0x60, 0x7f));
|
dispatches = dispatches.concat(r(0x60, 0x7f));
|
||||||
for (let i = 0; i < dispatches.length; ++i) {
|
for (let i = 0; i < dispatches.length; ++i) {
|
||||||
parser.currentState = ParserState.ESCAPE;
|
parser.currentState = ParserState.ESCAPE;
|
||||||
parser.parse(dispatches[i]);
|
parse(parser, dispatches[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
||||||
testTerminal.compare([['esc', '', dispatches[i]]]);
|
testTerminal.compare([['esc', '', dispatches[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -319,7 +334,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = r(0x20, 0x30);
|
const collect = r(0x20, 0x30);
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.ESCAPE;
|
parser.currentState = ParserState.ESCAPE;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE);
|
chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE);
|
||||||
chai.expect(parser.collect).equal(collect[i]);
|
chai.expect(parser.collect).equal(collect[i]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -328,12 +343,12 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('state ESCAPE_INTERMEDIATE execute rules', function (): void {
|
it('state ESCAPE_INTERMEDIATE execute rules', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
const exes = r(0x00, 0x18);
|
let exes = r(0x00, 0x18);
|
||||||
exes.concat(['\x19']);
|
exes = exes.concat(['\x19']);
|
||||||
exes.concat(r(0x1c, 0x20));
|
exes = exes.concat(r(0x1c, 0x20));
|
||||||
for (let i = 0; i < exes.length; ++i) {
|
for (let i = 0; i < exes.length; ++i) {
|
||||||
parser.currentState = ParserState.ESCAPE_INTERMEDIATE;
|
parser.currentState = ParserState.ESCAPE_INTERMEDIATE;
|
||||||
parser.parse(exes[i]);
|
parse(parser, exes[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE);
|
chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE);
|
||||||
testTerminal.compare([['exe', exes[i]]]);
|
testTerminal.compare([['exe', exes[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -344,7 +359,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
parser.currentState = ParserState.ESCAPE_INTERMEDIATE;
|
parser.currentState = ParserState.ESCAPE_INTERMEDIATE;
|
||||||
parser.parse('\x7f');
|
parse(parser, '\x7f');
|
||||||
chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE);
|
chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE);
|
||||||
testTerminal.compare([]);
|
testTerminal.compare([]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -355,7 +370,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = r(0x20, 0x30);
|
const collect = r(0x20, 0x30);
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.ESCAPE_INTERMEDIATE;
|
parser.currentState = ParserState.ESCAPE_INTERMEDIATE;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE);
|
chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE);
|
||||||
chai.expect(parser.collect).equal(collect[i]);
|
chai.expect(parser.collect).equal(collect[i]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -367,7 +382,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = r(0x30, 0x7f);
|
const collect = r(0x30, 0x7f);
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.ESCAPE_INTERMEDIATE;
|
parser.currentState = ParserState.ESCAPE_INTERMEDIATE;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
||||||
// '\x5c' --> ESC + \ (7bit ST) parser does not expose this as it already got handled
|
// '\x5c' --> ESC + \ (7bit ST) parser does not expose this as it already got handled
|
||||||
testTerminal.compare((collect[i] === '\x5c') ? [] : [['esc', '', collect[i]]]);
|
testTerminal.compare((collect[i] === '\x5c') ? [] : [['esc', '', collect[i]]]);
|
||||||
@@ -382,7 +397,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.osc = '#';
|
parser.osc = '#';
|
||||||
parser.params = [123];
|
parser.params = [123];
|
||||||
parser.collect = '#';
|
parser.collect = '#';
|
||||||
parser.parse('[');
|
parse(parser, '[');
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY);
|
chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY);
|
||||||
chai.expect(parser.osc).equal('');
|
chai.expect(parser.osc).equal('');
|
||||||
chai.expect(parser.params).eql([0]);
|
chai.expect(parser.params).eql([0]);
|
||||||
@@ -394,7 +409,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.osc = '#';
|
parser.osc = '#';
|
||||||
parser.params = [123];
|
parser.params = [123];
|
||||||
parser.collect = '#';
|
parser.collect = '#';
|
||||||
parser.parse('\x9b');
|
parse(parser, '\x9b');
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY);
|
chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY);
|
||||||
chai.expect(parser.osc).equal('');
|
chai.expect(parser.osc).equal('');
|
||||||
chai.expect(parser.params).eql([0]);
|
chai.expect(parser.params).eql([0]);
|
||||||
@@ -405,12 +420,12 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('state CSI_ENTRY execute rules', function (): void {
|
it('state CSI_ENTRY execute rules', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
const exes = r(0x00, 0x18);
|
let exes = r(0x00, 0x18);
|
||||||
exes.concat(['\x19']);
|
exes = exes.concat(['\x19']);
|
||||||
exes.concat(r(0x1c, 0x20));
|
exes = exes.concat(r(0x1c, 0x20));
|
||||||
for (let i = 0; i < exes.length; ++i) {
|
for (let i = 0; i < exes.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_ENTRY;
|
parser.currentState = ParserState.CSI_ENTRY;
|
||||||
parser.parse(exes[i]);
|
parse(parser, exes[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY);
|
chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY);
|
||||||
testTerminal.compare([['exe', exes[i]]]);
|
testTerminal.compare([['exe', exes[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -421,7 +436,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
parser.currentState = ParserState.CSI_ENTRY;
|
parser.currentState = ParserState.CSI_ENTRY;
|
||||||
parser.parse('\x7f');
|
parse(parser, '\x7f');
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY);
|
chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY);
|
||||||
testTerminal.compare([]);
|
testTerminal.compare([]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -432,7 +447,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const dispatches = r(0x40, 0x7f);
|
const dispatches = r(0x40, 0x7f);
|
||||||
for (let i = 0; i < dispatches.length; ++i) {
|
for (let i = 0; i < dispatches.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_ENTRY;
|
parser.currentState = ParserState.CSI_ENTRY;
|
||||||
parser.parse(dispatches[i]);
|
parse(parser, dispatches[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
||||||
testTerminal.compare([['csi', '', [0], dispatches[i]]]);
|
testTerminal.compare([['csi', '', [0], dispatches[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -445,19 +460,19 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = ['\x3c', '\x3d', '\x3e', '\x3f'];
|
const collect = ['\x3c', '\x3d', '\x3e', '\x3f'];
|
||||||
for (let i = 0; i < params.length; ++i) {
|
for (let i = 0; i < params.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_ENTRY;
|
parser.currentState = ParserState.CSI_ENTRY;
|
||||||
parser.parse(params[i]);
|
parse(parser, params[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
||||||
chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]);
|
chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
parser.currentState = ParserState.CSI_ENTRY;
|
parser.currentState = ParserState.CSI_ENTRY;
|
||||||
parser.parse('\x3b');
|
parse(parser, '\x3b');
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
||||||
chai.expect(parser.params).eql([0, 0]);
|
chai.expect(parser.params).eql([0, 0]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_ENTRY;
|
parser.currentState = ParserState.CSI_ENTRY;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
||||||
chai.expect(parser.collect).equal(collect[i]);
|
chai.expect(parser.collect).equal(collect[i]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -466,12 +481,12 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('state CSI_PARAM execute rules', function (): void {
|
it('state CSI_PARAM execute rules', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
const exes = r(0x00, 0x18);
|
let exes = r(0x00, 0x18);
|
||||||
exes.concat(['\x19']);
|
exes = exes.concat(['\x19']);
|
||||||
exes.concat(r(0x1c, 0x20));
|
exes = exes.concat(r(0x1c, 0x20));
|
||||||
for (let i = 0; i < exes.length; ++i) {
|
for (let i = 0; i < exes.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_PARAM;
|
parser.currentState = ParserState.CSI_PARAM;
|
||||||
parser.parse(exes[i]);
|
parse(parser, exes[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
||||||
testTerminal.compare([['exe', exes[i]]]);
|
testTerminal.compare([['exe', exes[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -483,13 +498,13 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39'];
|
const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39'];
|
||||||
for (let i = 0; i < params.length; ++i) {
|
for (let i = 0; i < params.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_PARAM;
|
parser.currentState = ParserState.CSI_PARAM;
|
||||||
parser.parse(params[i]);
|
parse(parser, params[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
||||||
chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]);
|
chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
parser.currentState = ParserState.CSI_PARAM;
|
parser.currentState = ParserState.CSI_PARAM;
|
||||||
parser.parse('\x3b');
|
parse(parser, '\x3b');
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
||||||
chai.expect(parser.params).eql([0, 0]);
|
chai.expect(parser.params).eql([0, 0]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -498,7 +513,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
parser.currentState = ParserState.CSI_PARAM;
|
parser.currentState = ParserState.CSI_PARAM;
|
||||||
parser.parse('\x7f');
|
parse(parser, '\x7f');
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
chai.expect(parser.currentState).equal(ParserState.CSI_PARAM);
|
||||||
testTerminal.compare([]);
|
testTerminal.compare([]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -510,7 +525,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
for (let i = 0; i < dispatches.length; ++i) {
|
for (let i = 0; i < dispatches.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_PARAM;
|
parser.currentState = ParserState.CSI_PARAM;
|
||||||
parser.params = [0, 1];
|
parser.params = [0, 1];
|
||||||
parser.parse(dispatches[i]);
|
parse(parser, dispatches[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
||||||
testTerminal.compare([['csi', '', [0, 1], dispatches[i]]]);
|
testTerminal.compare([['csi', '', [0, 1], dispatches[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -522,7 +537,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = r(0x20, 0x30);
|
const collect = r(0x20, 0x30);
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_ENTRY;
|
parser.currentState = ParserState.CSI_ENTRY;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE);
|
chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE);
|
||||||
chai.expect(parser.collect).equal(collect[i]);
|
chai.expect(parser.collect).equal(collect[i]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -533,7 +548,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = r(0x20, 0x30);
|
const collect = r(0x20, 0x30);
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_PARAM;
|
parser.currentState = ParserState.CSI_PARAM;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE);
|
chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE);
|
||||||
chai.expect(parser.collect).equal(collect[i]);
|
chai.expect(parser.collect).equal(collect[i]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -542,12 +557,12 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('state CSI_INTERMEDIATE execute rules', function (): void {
|
it('state CSI_INTERMEDIATE execute rules', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
const exes = r(0x00, 0x18);
|
let exes = r(0x00, 0x18);
|
||||||
exes.concat(['\x19']);
|
exes = exes.concat(['\x19']);
|
||||||
exes.concat(r(0x1c, 0x20));
|
exes = exes.concat(r(0x1c, 0x20));
|
||||||
for (let i = 0; i < exes.length; ++i) {
|
for (let i = 0; i < exes.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_INTERMEDIATE;
|
parser.currentState = ParserState.CSI_INTERMEDIATE;
|
||||||
parser.parse(exes[i]);
|
parse(parser, exes[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE);
|
chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE);
|
||||||
testTerminal.compare([['exe', exes[i]]]);
|
testTerminal.compare([['exe', exes[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -559,7 +574,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = r(0x20, 0x30);
|
const collect = r(0x20, 0x30);
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_INTERMEDIATE;
|
parser.currentState = ParserState.CSI_INTERMEDIATE;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE);
|
chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE);
|
||||||
chai.expect(parser.collect).equal(collect[i]);
|
chai.expect(parser.collect).equal(collect[i]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -569,7 +584,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
parser.currentState = ParserState.CSI_INTERMEDIATE;
|
parser.currentState = ParserState.CSI_INTERMEDIATE;
|
||||||
parser.parse('\x7f');
|
parse(parser, '\x7f');
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE);
|
chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE);
|
||||||
testTerminal.compare([]);
|
testTerminal.compare([]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -581,7 +596,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
for (let i = 0; i < dispatches.length; ++i) {
|
for (let i = 0; i < dispatches.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_INTERMEDIATE;
|
parser.currentState = ParserState.CSI_INTERMEDIATE;
|
||||||
parser.params = [0, 1];
|
parser.params = [0, 1];
|
||||||
parser.parse(dispatches[i]);
|
parse(parser, dispatches[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
||||||
testTerminal.compare([['csi', '', [0, 1], dispatches[i]]]);
|
testTerminal.compare([['csi', '', [0, 1], dispatches[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -591,7 +606,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('trans CSI_ENTRY --> CSI_IGNORE', function (): void {
|
it('trans CSI_ENTRY --> CSI_IGNORE', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
parser.currentState = ParserState.CSI_ENTRY;
|
parser.currentState = ParserState.CSI_ENTRY;
|
||||||
parser.parse('\x3a');
|
parse(parser, '\x3a');
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE);
|
chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
});
|
});
|
||||||
@@ -600,7 +615,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const chars = ['\x3a', '\x3c', '\x3d', '\x3e', '\x3f'];
|
const chars = ['\x3a', '\x3c', '\x3d', '\x3e', '\x3f'];
|
||||||
for (let i = 0; i < chars.length; ++i) {
|
for (let i = 0; i < chars.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_PARAM;
|
parser.currentState = ParserState.CSI_PARAM;
|
||||||
parser.parse('\x3b' + chars[i]);
|
parse(parser, '\x3b' + chars[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE);
|
chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE);
|
||||||
chai.expect(parser.params).eql([0, 0]);
|
chai.expect(parser.params).eql([0, 0]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -611,7 +626,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const chars = r(0x30, 0x40);
|
const chars = r(0x30, 0x40);
|
||||||
for (let i = 0; i < chars.length; ++i) {
|
for (let i = 0; i < chars.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_INTERMEDIATE;
|
parser.currentState = ParserState.CSI_INTERMEDIATE;
|
||||||
parser.parse(chars[i]);
|
parse(parser, chars[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE);
|
chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE);
|
||||||
chai.expect(parser.params).eql([0]);
|
chai.expect(parser.params).eql([0]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -620,12 +635,12 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('state CSI_IGNORE execute rules', function (): void {
|
it('state CSI_IGNORE execute rules', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
const exes = r(0x00, 0x18);
|
let exes = r(0x00, 0x18);
|
||||||
exes.concat(['\x19']);
|
exes = exes.concat(['\x19']);
|
||||||
exes.concat(r(0x1c, 0x20));
|
exes = exes.concat(r(0x1c, 0x20));
|
||||||
for (let i = 0; i < exes.length; ++i) {
|
for (let i = 0; i < exes.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_IGNORE;
|
parser.currentState = ParserState.CSI_IGNORE;
|
||||||
parser.parse(exes[i]);
|
parse(parser, exes[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE);
|
chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE);
|
||||||
testTerminal.compare([['exe', exes[i]]]);
|
testTerminal.compare([['exe', exes[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -635,11 +650,11 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('state CSI_IGNORE ignore', function (): void {
|
it('state CSI_IGNORE ignore', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
const ignored = r(0x20, 0x40);
|
let ignored = r(0x20, 0x40);
|
||||||
ignored.concat(['\x7f']);
|
ignored = ignored.concat(['\x7f']);
|
||||||
for (let i = 0; i < ignored.length; ++i) {
|
for (let i = 0; i < ignored.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_IGNORE;
|
parser.currentState = ParserState.CSI_IGNORE;
|
||||||
parser.parse(ignored[i]);
|
parse(parser, ignored[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE);
|
chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE);
|
||||||
testTerminal.compare([]);
|
testTerminal.compare([]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -652,7 +667,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
for (let i = 0; i < dispatches.length; ++i) {
|
for (let i = 0; i < dispatches.length; ++i) {
|
||||||
parser.currentState = ParserState.CSI_IGNORE;
|
parser.currentState = ParserState.CSI_IGNORE;
|
||||||
parser.params = [0, 1];
|
parser.params = [0, 1];
|
||||||
parser.parse(dispatches[i]);
|
parse(parser, dispatches[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
||||||
testTerminal.compare([]);
|
testTerminal.compare([]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -664,7 +679,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
// C0
|
// C0
|
||||||
let initializers = ['\x58', '\x5e', '\x5f'];
|
let initializers = ['\x58', '\x5e', '\x5f'];
|
||||||
for (let i = 0; i < initializers.length; ++i) {
|
for (let i = 0; i < initializers.length; ++i) {
|
||||||
parser.parse('\x1b' + initializers[i]);
|
parse(parser, '\x1b' + initializers[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING);
|
chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
@@ -673,7 +688,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.currentState = state;
|
parser.currentState = state;
|
||||||
initializers = ['\x98', '\x9e', '\x9f'];
|
initializers = ['\x98', '\x9e', '\x9f'];
|
||||||
for (let i = 0; i < initializers.length; ++i) {
|
for (let i = 0; i < initializers.length; ++i) {
|
||||||
parser.parse(initializers[i]);
|
parse(parser, initializers[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING);
|
chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
@@ -681,13 +696,13 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
});
|
});
|
||||||
it('state SOS_PM_APC_STRING ignore rules', function (): void {
|
it('state SOS_PM_APC_STRING ignore rules', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
const ignored = r(0x00, 0x18);
|
let ignored = r(0x00, 0x18);
|
||||||
ignored.concat(['\x19']);
|
ignored = ignored.concat(['\x19']);
|
||||||
ignored.concat(r(0x1c, 0x20));
|
ignored = ignored.concat(r(0x1c, 0x20));
|
||||||
ignored.concat(r(0x20, 0x80));
|
ignored = ignored.concat(r(0x20, 0x80));
|
||||||
for (let i = 0; i < ignored.length; ++i) {
|
for (let i = 0; i < ignored.length; ++i) {
|
||||||
parser.currentState = ParserState.SOS_PM_APC_STRING;
|
parser.currentState = ParserState.SOS_PM_APC_STRING;
|
||||||
parser.parse(ignored[i]);
|
parse(parser, ignored[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING);
|
chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
@@ -695,13 +710,13 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('trans ANYWHERE/ESCAPE --> OSC_STRING', function (): void {
|
it('trans ANYWHERE/ESCAPE --> OSC_STRING', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
// C0
|
// C0
|
||||||
parser.parse('\x1b]');
|
parse(parser, '\x1b]');
|
||||||
chai.expect(parser.currentState).equal(ParserState.OSC_STRING);
|
chai.expect(parser.currentState).equal(ParserState.OSC_STRING);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
// C1
|
// C1
|
||||||
for (state in states) {
|
for (state in states) {
|
||||||
parser.currentState = state;
|
parser.currentState = state;
|
||||||
parser.parse('\x9d');
|
parse(parser, '\x9d');
|
||||||
chai.expect(parser.currentState).equal(ParserState.OSC_STRING);
|
chai.expect(parser.currentState).equal(ParserState.OSC_STRING);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
@@ -714,7 +729,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
'\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f'];
|
'\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f'];
|
||||||
for (let i = 0; i < ignored.length; ++i) {
|
for (let i = 0; i < ignored.length; ++i) {
|
||||||
parser.currentState = ParserState.OSC_STRING;
|
parser.currentState = ParserState.OSC_STRING;
|
||||||
parser.parse(ignored[i]);
|
parse(parser, ignored[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.OSC_STRING);
|
chai.expect(parser.currentState).equal(ParserState.OSC_STRING);
|
||||||
chai.expect(parser.osc).equal('');
|
chai.expect(parser.osc).equal('');
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -725,7 +740,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const puts = r(0x20, 0x80);
|
const puts = r(0x20, 0x80);
|
||||||
for (let i = 0; i < puts.length; ++i) {
|
for (let i = 0; i < puts.length; ++i) {
|
||||||
parser.currentState = ParserState.OSC_STRING;
|
parser.currentState = ParserState.OSC_STRING;
|
||||||
parser.parse(puts[i]);
|
parse(parser, puts[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.OSC_STRING);
|
chai.expect(parser.currentState).equal(ParserState.OSC_STRING);
|
||||||
chai.expect(parser.osc).equal(puts[i]);
|
chai.expect(parser.osc).equal(puts[i]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -734,13 +749,13 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('state DCS_ENTRY', function (): void {
|
it('state DCS_ENTRY', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
// C0
|
// C0
|
||||||
parser.parse('\x1bP');
|
parse(parser, '\x1bP');
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY);
|
chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
// C1
|
// C1
|
||||||
for (state in states) {
|
for (state in states) {
|
||||||
parser.currentState = state;
|
parser.currentState = state;
|
||||||
parser.parse('\x90');
|
parse(parser, '\x90');
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY);
|
chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
@@ -753,7 +768,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
'\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f'];
|
'\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f'];
|
||||||
for (let i = 0; i < ignored.length; ++i) {
|
for (let i = 0; i < ignored.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_ENTRY;
|
parser.currentState = ParserState.DCS_ENTRY;
|
||||||
parser.parse(ignored[i]);
|
parse(parser, ignored[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY);
|
chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
@@ -764,19 +779,19 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = ['\x3c', '\x3d', '\x3e', '\x3f'];
|
const collect = ['\x3c', '\x3d', '\x3e', '\x3f'];
|
||||||
for (let i = 0; i < params.length; ++i) {
|
for (let i = 0; i < params.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_ENTRY;
|
parser.currentState = ParserState.DCS_ENTRY;
|
||||||
parser.parse(params[i]);
|
parse(parser, params[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_PARAM);
|
chai.expect(parser.currentState).equal(ParserState.DCS_PARAM);
|
||||||
chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]);
|
chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
parser.currentState = ParserState.DCS_ENTRY;
|
parser.currentState = ParserState.DCS_ENTRY;
|
||||||
parser.parse('\x3b');
|
parse(parser, '\x3b');
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_PARAM);
|
chai.expect(parser.currentState).equal(ParserState.DCS_PARAM);
|
||||||
chai.expect(parser.params).eql([0, 0]);
|
chai.expect(parser.params).eql([0, 0]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_ENTRY;
|
parser.currentState = ParserState.DCS_ENTRY;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_PARAM);
|
chai.expect(parser.currentState).equal(ParserState.DCS_PARAM);
|
||||||
chai.expect(parser.collect).equal(collect[i]);
|
chai.expect(parser.collect).equal(collect[i]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -790,7 +805,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
'\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f'];
|
'\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f'];
|
||||||
for (let i = 0; i < ignored.length; ++i) {
|
for (let i = 0; i < ignored.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_PARAM;
|
parser.currentState = ParserState.DCS_PARAM;
|
||||||
parser.parse(ignored[i]);
|
parse(parser, ignored[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_PARAM);
|
chai.expect(parser.currentState).equal(ParserState.DCS_PARAM);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
@@ -800,13 +815,13 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39'];
|
const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39'];
|
||||||
for (let i = 0; i < params.length; ++i) {
|
for (let i = 0; i < params.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_PARAM;
|
parser.currentState = ParserState.DCS_PARAM;
|
||||||
parser.parse(params[i]);
|
parse(parser, params[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_PARAM);
|
chai.expect(parser.currentState).equal(ParserState.DCS_PARAM);
|
||||||
chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]);
|
chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
parser.currentState = ParserState.DCS_PARAM;
|
parser.currentState = ParserState.DCS_PARAM;
|
||||||
parser.parse('\x3b');
|
parse(parser, '\x3b');
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_PARAM);
|
chai.expect(parser.currentState).equal(ParserState.DCS_PARAM);
|
||||||
chai.expect(parser.params).eql([0, 0]);
|
chai.expect(parser.params).eql([0, 0]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -814,7 +829,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('trans DCS_ENTRY --> DCS_IGNORE', function (): void {
|
it('trans DCS_ENTRY --> DCS_IGNORE', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
parser.currentState = ParserState.DCS_ENTRY;
|
parser.currentState = ParserState.DCS_ENTRY;
|
||||||
parser.parse('\x3a');
|
parse(parser, '\x3a');
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE);
|
chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
});
|
});
|
||||||
@@ -823,7 +838,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const chars = ['\x3a', '\x3c', '\x3d', '\x3e', '\x3f'];
|
const chars = ['\x3a', '\x3c', '\x3d', '\x3e', '\x3f'];
|
||||||
for (let i = 0; i < chars.length; ++i) {
|
for (let i = 0; i < chars.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_PARAM;
|
parser.currentState = ParserState.DCS_PARAM;
|
||||||
parser.parse('\x3b' + chars[i]);
|
parse(parser, '\x3b' + chars[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE);
|
chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE);
|
||||||
chai.expect(parser.params).eql([0, 0]);
|
chai.expect(parser.params).eql([0, 0]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -834,21 +849,21 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const chars = r(0x30, 0x40);
|
const chars = r(0x30, 0x40);
|
||||||
for (let i = 0; i < chars.length; ++i) {
|
for (let i = 0; i < chars.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_INTERMEDIATE;
|
parser.currentState = ParserState.DCS_INTERMEDIATE;
|
||||||
parser.parse(chars[i]);
|
parse(parser, chars[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE);
|
chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
it('state DCS_IGNORE ignore rules', function (): void {
|
it('state DCS_IGNORE ignore rules', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
const ignored = [
|
let ignored = [
|
||||||
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08',
|
'\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08',
|
||||||
'\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11',
|
'\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11',
|
||||||
'\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f'];
|
'\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f'];
|
||||||
ignored.concat(r(0x20, 0x80));
|
ignored = ignored.concat(r(0x20, 0x80));
|
||||||
for (let i = 0; i < ignored.length; ++i) {
|
for (let i = 0; i < ignored.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_IGNORE;
|
parser.currentState = ParserState.DCS_IGNORE;
|
||||||
parser.parse(ignored[i]);
|
parse(parser, ignored[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE);
|
chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
@@ -858,7 +873,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = r(0x20, 0x30);
|
const collect = r(0x20, 0x30);
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_ENTRY;
|
parser.currentState = ParserState.DCS_ENTRY;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE);
|
chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE);
|
||||||
chai.expect(parser.collect).equal(collect[i]);
|
chai.expect(parser.collect).equal(collect[i]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -869,7 +884,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = r(0x20, 0x30);
|
const collect = r(0x20, 0x30);
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_PARAM;
|
parser.currentState = ParserState.DCS_PARAM;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE);
|
chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE);
|
||||||
chai.expect(parser.collect).equal(collect[i]);
|
chai.expect(parser.collect).equal(collect[i]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -883,7 +898,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
'\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f'];
|
'\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f'];
|
||||||
for (let i = 0; i < ignored.length; ++i) {
|
for (let i = 0; i < ignored.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_INTERMEDIATE;
|
parser.currentState = ParserState.DCS_INTERMEDIATE;
|
||||||
parser.parse(ignored[i]);
|
parse(parser, ignored[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE);
|
chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
}
|
}
|
||||||
@@ -893,7 +908,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = r(0x20, 0x30);
|
const collect = r(0x20, 0x30);
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_INTERMEDIATE;
|
parser.currentState = ParserState.DCS_INTERMEDIATE;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE);
|
chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE);
|
||||||
chai.expect(parser.collect).equal(collect[i]);
|
chai.expect(parser.collect).equal(collect[i]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -904,7 +919,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const chars = r(0x30, 0x40);
|
const chars = r(0x30, 0x40);
|
||||||
for (let i = 0; i < chars.length; ++i) {
|
for (let i = 0; i < chars.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_INTERMEDIATE;
|
parser.currentState = ParserState.DCS_INTERMEDIATE;
|
||||||
parser.parse('\x20' + chars[i]);
|
parse(parser, '\x20' + chars[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE);
|
chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE);
|
||||||
chai.expect(parser.collect).equal('\x20');
|
chai.expect(parser.collect).equal('\x20');
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -916,7 +931,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = r(0x40, 0x7f);
|
const collect = r(0x40, 0x7f);
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_ENTRY;
|
parser.currentState = ParserState.DCS_ENTRY;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH);
|
chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH);
|
||||||
testTerminal.compare([['dcs hook', '', [0], collect[i]]]);
|
testTerminal.compare([['dcs hook', '', [0], collect[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -929,7 +944,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = r(0x40, 0x7f);
|
const collect = r(0x40, 0x7f);
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_PARAM;
|
parser.currentState = ParserState.DCS_PARAM;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH);
|
chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH);
|
||||||
testTerminal.compare([['dcs hook', '', [0], collect[i]]]);
|
testTerminal.compare([['dcs hook', '', [0], collect[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -942,7 +957,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const collect = r(0x40, 0x7f);
|
const collect = r(0x40, 0x7f);
|
||||||
for (let i = 0; i < collect.length; ++i) {
|
for (let i = 0; i < collect.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_INTERMEDIATE;
|
parser.currentState = ParserState.DCS_INTERMEDIATE;
|
||||||
parser.parse(collect[i]);
|
parse(parser, collect[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH);
|
chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH);
|
||||||
testTerminal.compare([['dcs hook', '', [0], collect[i]]]);
|
testTerminal.compare([['dcs hook', '', [0], collect[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -952,14 +967,14 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
it('state DCS_PASSTHROUGH put action', function (): void {
|
it('state DCS_PASSTHROUGH put action', function (): void {
|
||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
const puts = r(0x00, 0x18);
|
let puts = r(0x00, 0x18);
|
||||||
puts.concat(['\x19']);
|
puts = puts.concat(['\x19']);
|
||||||
puts.concat(r(0x1c, 0x20));
|
puts = puts.concat(r(0x1c, 0x20));
|
||||||
puts.concat(r(0x20, 0x7f));
|
puts = puts.concat(r(0x20, 0x7f));
|
||||||
for (let i = 0; i < puts.length; ++i) {
|
for (let i = 0; i < puts.length; ++i) {
|
||||||
parser.currentState = ParserState.DCS_PASSTHROUGH;
|
parser.currentState = ParserState.DCS_PASSTHROUGH;
|
||||||
parser.mockActiveDcsHandler();
|
parser.mockActiveDcsHandler();
|
||||||
parser.parse(puts[i]);
|
parse(parser, puts[i]);
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH);
|
chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH);
|
||||||
testTerminal.compare([['dcs put', puts[i]]]);
|
testTerminal.compare([['dcs put', puts[i]]]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -970,7 +985,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
parser.currentState = ParserState.DCS_PASSTHROUGH;
|
parser.currentState = ParserState.DCS_PASSTHROUGH;
|
||||||
parser.parse('\x7f');
|
parse(parser, '\x7f');
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH);
|
chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH);
|
||||||
testTerminal.compare([]);
|
testTerminal.compare([]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -989,7 +1004,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
}
|
}
|
||||||
parser.parse(s);
|
parse(parser, s);
|
||||||
testTerminal.compare(value);
|
testTerminal.compare(value);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -1067,7 +1082,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
parser.currentState = ParserState.CSI_IGNORE;
|
parser.currentState = ParserState.CSI_IGNORE;
|
||||||
parser.parse('€öäü');
|
parse(parser, '€öäü');
|
||||||
chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE);
|
chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE);
|
||||||
testTerminal.compare([]);
|
testTerminal.compare([]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -1077,7 +1092,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
parser.currentState = ParserState.DCS_IGNORE;
|
parser.currentState = ParserState.DCS_IGNORE;
|
||||||
parser.parse('€öäü');
|
parse(parser, '€öäü');
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE);
|
chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE);
|
||||||
testTerminal.compare([]);
|
testTerminal.compare([]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -1087,7 +1102,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
parser.currentState = ParserState.DCS_PASSTHROUGH;
|
parser.currentState = ParserState.DCS_PASSTHROUGH;
|
||||||
parser.parse('\x901;2;3+$a€öäü');
|
parse(parser, '\x901;2;3+$a€öäü');
|
||||||
chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH);
|
chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH);
|
||||||
testTerminal.compare([['dcs hook', '+$', [1, 2, 3], 'a'], ['dcs put', '€öäü']]);
|
testTerminal.compare([['dcs hook', '+$', [1, 2, 3], 'a'], ['dcs put', '€öäü']]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -1097,7 +1112,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser.reset();
|
parser.reset();
|
||||||
testTerminal.clear();
|
testTerminal.clear();
|
||||||
parser.currentState = ParserState.GROUND;
|
parser.currentState = ParserState.GROUND;
|
||||||
parser.parse('\x9c');
|
parse(parser, '\x9c');
|
||||||
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
chai.expect(parser.currentState).equal(ParserState.GROUND);
|
||||||
testTerminal.compare([]);
|
testTerminal.compare([]);
|
||||||
parser.reset();
|
parser.reset();
|
||||||
@@ -1127,15 +1142,17 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
clearAccu();
|
clearAccu();
|
||||||
});
|
});
|
||||||
it('print handler', function (): void {
|
it('print handler', function (): void {
|
||||||
parser2.setPrintHandler(function (data: string, start: number, end: number): void {
|
parser2.setPrintHandler(function (data: Uint32Array, start: number, end: number): void {
|
||||||
print += data.substring(start, end);
|
for (let i = start; i < end; ++i) {
|
||||||
|
print += stringFromCodePoint(data[i]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(print).equal('hello world!$>');
|
chai.expect(print).equal('hello world!$>');
|
||||||
parser2.clearPrintHandler();
|
parser2.clearPrintHandler();
|
||||||
parser2.clearPrintHandler(); // should not throw
|
parser2.clearPrintHandler(); // should not throw
|
||||||
clearAccu();
|
clearAccu();
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(print).equal('');
|
chai.expect(print).equal('');
|
||||||
});
|
});
|
||||||
it('ESC handler', function (): void {
|
it('ESC handler', function (): void {
|
||||||
@@ -1145,28 +1162,28 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser2.setEscHandler('E', function (): void {
|
parser2.setEscHandler('E', function (): void {
|
||||||
esc.push('E');
|
esc.push('E');
|
||||||
});
|
});
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(esc).eql(['%G', 'E']);
|
chai.expect(esc).eql(['%G', 'E']);
|
||||||
parser2.clearEscHandler('%G');
|
parser2.clearEscHandler('%G');
|
||||||
parser2.clearEscHandler('%G'); // should not throw
|
parser2.clearEscHandler('%G'); // should not throw
|
||||||
clearAccu();
|
clearAccu();
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(esc).eql(['E']);
|
chai.expect(esc).eql(['E']);
|
||||||
parser2.clearEscHandler('E');
|
parser2.clearEscHandler('E');
|
||||||
clearAccu();
|
clearAccu();
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(esc).eql([]);
|
chai.expect(esc).eql([]);
|
||||||
});
|
});
|
||||||
it('CSI handler', function (): void {
|
it('CSI handler', function (): void {
|
||||||
parser2.setCsiHandler('m', function (params: number[], collect: string): void {
|
parser2.setCsiHandler('m', function (params: number[], collect: string): void {
|
||||||
csi.push(['m', params, collect]);
|
csi.push(['m', params, collect]);
|
||||||
});
|
});
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||||
parser2.clearCsiHandler('m');
|
parser2.clearCsiHandler('m');
|
||||||
parser2.clearCsiHandler('m'); // should not throw
|
parser2.clearCsiHandler('m'); // should not throw
|
||||||
clearAccu();
|
clearAccu();
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(csi).eql([]);
|
chai.expect(csi).eql([]);
|
||||||
});
|
});
|
||||||
describe('CSI custom handlers', () => {
|
describe('CSI custom handlers', () => {
|
||||||
@@ -1174,7 +1191,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const csiCustom: [string, number[], string][] = [];
|
const csiCustom: [string, number[], string][] = [];
|
||||||
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
||||||
parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; });
|
parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; });
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(csi).eql([], 'Should not fallback to original handler');
|
chai.expect(csi).eql([], 'Should not fallback to original handler');
|
||||||
chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||||
});
|
});
|
||||||
@@ -1182,7 +1199,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const csiCustom: [string, number[], string][] = [];
|
const csiCustom: [string, number[], string][] = [];
|
||||||
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
||||||
parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return false; });
|
parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return false; });
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']], 'Should fallback to original handler');
|
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']], 'Should fallback to original handler');
|
||||||
chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||||
});
|
});
|
||||||
@@ -1192,7 +1209,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
||||||
parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; });
|
parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; });
|
||||||
parser2.addCsiHandler('m', (params, collect) => { csiCustom2.push(['m', params, collect]); return false; });
|
parser2.addCsiHandler('m', (params, collect) => { csiCustom2.push(['m', params, collect]); return false; });
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(csi).eql([], 'Should not fallback to original handler');
|
chai.expect(csi).eql([], 'Should not fallback to original handler');
|
||||||
chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||||
chai.expect(csiCustom2).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
chai.expect(csiCustom2).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||||
@@ -1203,7 +1220,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
||||||
parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; });
|
parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; });
|
||||||
parser2.addCsiHandler('m', (params, collect) => { csiCustom2.push(['m', params, collect]); return true; });
|
parser2.addCsiHandler('m', (params, collect) => { csiCustom2.push(['m', params, collect]); return true; });
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(csi).eql([], 'Should not fallback to original handler');
|
chai.expect(csi).eql([], 'Should not fallback to original handler');
|
||||||
chai.expect(csiCustom).eql([], 'Should not fallback once');
|
chai.expect(csiCustom).eql([], 'Should not fallback once');
|
||||||
chai.expect(csiCustom2).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
chai.expect(csiCustom2).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||||
@@ -1213,7 +1230,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser2.setCsiHandler('m', () => order.push(1));
|
parser2.setCsiHandler('m', () => order.push(1));
|
||||||
parser2.addCsiHandler('m', () => { order.push(2); return false; });
|
parser2.addCsiHandler('m', () => { order.push(2); return false; });
|
||||||
parser2.addCsiHandler('m', () => { order.push(3); return false; });
|
parser2.addCsiHandler('m', () => { order.push(3); return false; });
|
||||||
parser2.parse('\x1b[0m');
|
parse(parser2, '\x1b[0m');
|
||||||
chai.expect(order).eql([3, 2, 1]);
|
chai.expect(order).eql([3, 2, 1]);
|
||||||
});
|
});
|
||||||
it('Dispose should work', () => {
|
it('Dispose should work', () => {
|
||||||
@@ -1221,7 +1238,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
||||||
const customHandler = parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; });
|
const customHandler = parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; });
|
||||||
customHandler.dispose();
|
customHandler.dispose();
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||||
chai.expect(csiCustom).eql([], 'Should not use custom handler as it was disposed');
|
chai.expect(csiCustom).eql([], 'Should not use custom handler as it was disposed');
|
||||||
});
|
});
|
||||||
@@ -1231,7 +1248,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const customHandler = parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; });
|
const customHandler = parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; });
|
||||||
customHandler.dispose();
|
customHandler.dispose();
|
||||||
customHandler.dispose();
|
customHandler.dispose();
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||||
chai.expect(csiCustom).eql([], 'Should not use custom handler as it was disposed');
|
chai.expect(csiCustom).eql([], 'Should not use custom handler as it was disposed');
|
||||||
});
|
});
|
||||||
@@ -1243,24 +1260,24 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser2.setExecuteHandler('\r', function (): void {
|
parser2.setExecuteHandler('\r', function (): void {
|
||||||
exe.push('\r');
|
exe.push('\r');
|
||||||
});
|
});
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(exe).eql(['\r', '\n']);
|
chai.expect(exe).eql(['\r', '\n']);
|
||||||
parser2.clearExecuteHandler('\r');
|
parser2.clearExecuteHandler('\r');
|
||||||
parser2.clearExecuteHandler('\r'); // should not throw
|
parser2.clearExecuteHandler('\r'); // should not throw
|
||||||
clearAccu();
|
clearAccu();
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(exe).eql(['\n']);
|
chai.expect(exe).eql(['\n']);
|
||||||
});
|
});
|
||||||
it('OSC handler', function (): void {
|
it('OSC handler', function (): void {
|
||||||
parser2.setOscHandler(1, function (data: string): void {
|
parser2.setOscHandler(1, function (data: string): void {
|
||||||
osc.push([1, data]);
|
osc.push([1, data]);
|
||||||
});
|
});
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(osc).eql([[1, 'foo=bar']]);
|
chai.expect(osc).eql([[1, 'foo=bar']]);
|
||||||
parser2.clearOscHandler(1);
|
parser2.clearOscHandler(1);
|
||||||
parser2.clearOscHandler(1); // should not throw
|
parser2.clearOscHandler(1); // should not throw
|
||||||
clearAccu();
|
clearAccu();
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(osc).eql([]);
|
chai.expect(osc).eql([]);
|
||||||
});
|
});
|
||||||
describe('OSC custom handlers', () => {
|
describe('OSC custom handlers', () => {
|
||||||
@@ -1268,7 +1285,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const oscCustom: [number, string][] = [];
|
const oscCustom: [number, string][] = [];
|
||||||
parser2.setOscHandler(1, data => osc.push([1, data]));
|
parser2.setOscHandler(1, data => osc.push([1, data]));
|
||||||
parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(osc).eql([], 'Should not fallback to original handler');
|
chai.expect(osc).eql([], 'Should not fallback to original handler');
|
||||||
chai.expect(oscCustom).eql([[1, 'foo=bar']]);
|
chai.expect(oscCustom).eql([[1, 'foo=bar']]);
|
||||||
});
|
});
|
||||||
@@ -1276,7 +1293,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const oscCustom: [number, string][] = [];
|
const oscCustom: [number, string][] = [];
|
||||||
parser2.setOscHandler(1, data => osc.push([1, data]));
|
parser2.setOscHandler(1, data => osc.push([1, data]));
|
||||||
parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return false; });
|
parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return false; });
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(osc).eql([[1, 'foo=bar']], 'Should fallback to original handler');
|
chai.expect(osc).eql([[1, 'foo=bar']], 'Should fallback to original handler');
|
||||||
chai.expect(oscCustom).eql([[1, 'foo=bar']]);
|
chai.expect(oscCustom).eql([[1, 'foo=bar']]);
|
||||||
});
|
});
|
||||||
@@ -1286,7 +1303,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser2.setOscHandler(1, data => osc.push([1, data]));
|
parser2.setOscHandler(1, data => osc.push([1, data]));
|
||||||
parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
||||||
parser2.addOscHandler(1, data => { oscCustom2.push([1, data]); return false; });
|
parser2.addOscHandler(1, data => { oscCustom2.push([1, data]); return false; });
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(osc).eql([], 'Should not fallback to original handler');
|
chai.expect(osc).eql([], 'Should not fallback to original handler');
|
||||||
chai.expect(oscCustom).eql([[1, 'foo=bar']]);
|
chai.expect(oscCustom).eql([[1, 'foo=bar']]);
|
||||||
chai.expect(oscCustom2).eql([[1, 'foo=bar']]);
|
chai.expect(oscCustom2).eql([[1, 'foo=bar']]);
|
||||||
@@ -1297,7 +1314,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser2.setOscHandler(1, data => osc.push([1, data]));
|
parser2.setOscHandler(1, data => osc.push([1, data]));
|
||||||
parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
||||||
parser2.addOscHandler(1, data => { oscCustom2.push([1, data]); return true; });
|
parser2.addOscHandler(1, data => { oscCustom2.push([1, data]); return true; });
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(osc).eql([], 'Should not fallback to original handler');
|
chai.expect(osc).eql([], 'Should not fallback to original handler');
|
||||||
chai.expect(oscCustom).eql([], 'Should not fallback once');
|
chai.expect(oscCustom).eql([], 'Should not fallback once');
|
||||||
chai.expect(oscCustom2).eql([[1, 'foo=bar']]);
|
chai.expect(oscCustom2).eql([[1, 'foo=bar']]);
|
||||||
@@ -1307,7 +1324,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser2.setOscHandler(1, () => order.push(1));
|
parser2.setOscHandler(1, () => order.push(1));
|
||||||
parser2.addOscHandler(1, () => { order.push(2); return false; });
|
parser2.addOscHandler(1, () => { order.push(2); return false; });
|
||||||
parser2.addOscHandler(1, () => { order.push(3); return false; });
|
parser2.addOscHandler(1, () => { order.push(3); return false; });
|
||||||
parser2.parse('\x1b]1;foo=bar\x1b\\');
|
parse(parser2, '\x1b]1;foo=bar\x1b\\');
|
||||||
chai.expect(order).eql([3, 2, 1]);
|
chai.expect(order).eql([3, 2, 1]);
|
||||||
});
|
});
|
||||||
it('Dispose should work', () => {
|
it('Dispose should work', () => {
|
||||||
@@ -1315,7 +1332,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser2.setOscHandler(1, data => osc.push([1, data]));
|
parser2.setOscHandler(1, data => osc.push([1, data]));
|
||||||
const customHandler = parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
const customHandler = parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
||||||
customHandler.dispose();
|
customHandler.dispose();
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(osc).eql([[1, 'foo=bar']]);
|
chai.expect(osc).eql([[1, 'foo=bar']]);
|
||||||
chai.expect(oscCustom).eql([], 'Should not use custom handler as it was disposed');
|
chai.expect(oscCustom).eql([], 'Should not use custom handler as it was disposed');
|
||||||
});
|
});
|
||||||
@@ -1325,7 +1342,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
const customHandler = parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
const customHandler = parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
||||||
customHandler.dispose();
|
customHandler.dispose();
|
||||||
customHandler.dispose();
|
customHandler.dispose();
|
||||||
parser2.parse(INPUT);
|
parse(parser2, INPUT);
|
||||||
chai.expect(osc).eql([[1, 'foo=bar']]);
|
chai.expect(osc).eql([[1, 'foo=bar']]);
|
||||||
chai.expect(oscCustom).eql([], 'Should not use custom handler as it was disposed');
|
chai.expect(oscCustom).eql([], 'Should not use custom handler as it was disposed');
|
||||||
});
|
});
|
||||||
@@ -1335,15 +1352,19 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
hook: function (collect: string, params: number[], flag: number): void {
|
hook: function (collect: string, params: number[], flag: number): void {
|
||||||
dcs.push(['hook', collect, params, flag]);
|
dcs.push(['hook', collect, params, flag]);
|
||||||
},
|
},
|
||||||
put: function (data: string, start: number, end: number): void {
|
put: function (data: Uint32Array, start: number, end: number): void {
|
||||||
dcs.push(['put', data.substring(start, end)]);
|
let s = '';
|
||||||
|
for (let i = start; i < end; ++i) {
|
||||||
|
s += stringFromCodePoint(data[i]);
|
||||||
|
}
|
||||||
|
dcs.push(['put', s]);
|
||||||
},
|
},
|
||||||
unhook: function (): void {
|
unhook: function (): void {
|
||||||
dcs.push(['unhook']);
|
dcs.push(['unhook']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
parser2.parse('\x1bP1;2;3+pabc');
|
parse(parser2, '\x1bP1;2;3+pabc');
|
||||||
parser2.parse(';de\x9c');
|
parse(parser2, ';de\x9c');
|
||||||
chai.expect(dcs).eql([
|
chai.expect(dcs).eql([
|
||||||
['hook', '+', [1, 2, 3], 'p'.charCodeAt(0)],
|
['hook', '+', [1, 2, 3], 'p'.charCodeAt(0)],
|
||||||
['put', 'abc'], ['put', ';de'],
|
['put', 'abc'], ['put', ';de'],
|
||||||
@@ -1352,8 +1373,8 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser2.clearDcsHandler('+p');
|
parser2.clearDcsHandler('+p');
|
||||||
parser2.clearDcsHandler('+p'); // should not throw
|
parser2.clearDcsHandler('+p'); // should not throw
|
||||||
clearAccu();
|
clearAccu();
|
||||||
parser2.parse('\x1bP1;2;3+pabc');
|
parse(parser2, '\x1bP1;2;3+pabc');
|
||||||
parser2.parse(';de\x9c');
|
parse(parser2, ';de\x9c');
|
||||||
chai.expect(dcs).eql([]);
|
chai.expect(dcs).eql([]);
|
||||||
});
|
});
|
||||||
it('ERROR handler', function (): void {
|
it('ERROR handler', function (): void {
|
||||||
@@ -1362,7 +1383,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
errorState = state;
|
errorState = state;
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
parser2.parse('\x1b[1;2;€;3m'); // faulty escape sequence
|
parse(parser2, '\x1b[1;2;€;3m'); // faulty escape sequence
|
||||||
chai.expect(errorState).eql({
|
chai.expect(errorState).eql({
|
||||||
position: 6,
|
position: 6,
|
||||||
code: '€'.charCodeAt(0),
|
code: '€'.charCodeAt(0),
|
||||||
@@ -1377,7 +1398,7 @@ describe('EscapeSequenceParser', function (): void {
|
|||||||
parser2.clearErrorHandler();
|
parser2.clearErrorHandler();
|
||||||
parser2.clearErrorHandler(); // should not throw
|
parser2.clearErrorHandler(); // should not throw
|
||||||
errorState = null;
|
errorState = null;
|
||||||
parser2.parse('\x1b[1;2;a;3m');
|
parse(parser2, '\x1b[1;2;a;3m');
|
||||||
chai.expect(errorState).eql(null);
|
chai.expect(errorState).eql(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+15
-14
@@ -6,6 +6,7 @@
|
|||||||
import { ParserState, ParserAction, IParsingState, IDcsHandler, IEscapeSequenceParser } from './Types';
|
import { ParserState, ParserAction, IParsingState, IDcsHandler, IEscapeSequenceParser } from './Types';
|
||||||
import { IDisposable } from 'xterm';
|
import { IDisposable } from 'xterm';
|
||||||
import { Disposable } from './common/Lifecycle';
|
import { Disposable } from './common/Lifecycle';
|
||||||
|
import { utf32ToString } from './core/input/TextDecoder';
|
||||||
|
|
||||||
interface IHandlerCollection<T> {
|
interface IHandlerCollection<T> {
|
||||||
[key: string]: T[];
|
[key: string]: T[];
|
||||||
@@ -134,6 +135,7 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable {
|
|||||||
table.addMany(PRINTABLES, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING);
|
table.addMany(PRINTABLES, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING);
|
||||||
table.addMany(EXECUTABLES, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING);
|
table.addMany(EXECUTABLES, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING);
|
||||||
table.add(0x9c, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.GROUND);
|
table.add(0x9c, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.GROUND);
|
||||||
|
table.add(0x7f, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING);
|
||||||
// csi entries
|
// csi entries
|
||||||
table.add(0x5b, ParserState.ESCAPE, ParserAction.CLEAR, ParserState.CSI_ENTRY);
|
table.add(0x5b, ParserState.ESCAPE, ParserAction.CLEAR, ParserState.CSI_ENTRY);
|
||||||
table.addMany(r(0x40, 0x7f), ParserState.CSI_ENTRY, ParserAction.CSI_DISPATCH, ParserState.GROUND);
|
table.addMany(r(0x40, 0x7f), ParserState.CSI_ENTRY, ParserAction.CSI_DISPATCH, ParserState.GROUND);
|
||||||
@@ -202,7 +204,7 @@ export const VT500_TRANSITION_TABLE = (function (): TransitionTable {
|
|||||||
*/
|
*/
|
||||||
class DcsDummy implements IDcsHandler {
|
class DcsDummy implements IDcsHandler {
|
||||||
hook(collect: string, params: number[], flag: number): void { }
|
hook(collect: string, params: number[], flag: number): void { }
|
||||||
put(data: string, start: number, end: number): void { }
|
put(data: Uint32Array, start: number, end: number): void { }
|
||||||
unhook(): void { }
|
unhook(): void { }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +230,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
|||||||
protected _collect: string;
|
protected _collect: string;
|
||||||
|
|
||||||
// handler lookup containers
|
// handler lookup containers
|
||||||
protected _printHandler: (data: string, start: number, end: number) => void;
|
protected _printHandler: (data: Uint32Array, start: number, end: number) => void;
|
||||||
protected _executeHandlers: any;
|
protected _executeHandlers: any;
|
||||||
protected _csiHandlers: IHandlerCollection<CsiHandler>;
|
protected _csiHandlers: IHandlerCollection<CsiHandler>;
|
||||||
protected _escHandlers: any;
|
protected _escHandlers: any;
|
||||||
@@ -238,7 +240,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
|||||||
protected _errorHandler: (state: IParsingState) => IParsingState;
|
protected _errorHandler: (state: IParsingState) => IParsingState;
|
||||||
|
|
||||||
// fallback handlers
|
// fallback handlers
|
||||||
protected _printHandlerFb: (data: string, start: number, end: number) => void;
|
protected _printHandlerFb: (data: Uint32Array, start: number, end: number) => void;
|
||||||
protected _executeHandlerFb: (code: number) => void;
|
protected _executeHandlerFb: (code: number) => void;
|
||||||
protected _csiHandlerFb: (collect: string, params: number[], flag: number) => void;
|
protected _csiHandlerFb: (collect: string, params: number[], flag: number) => void;
|
||||||
protected _escHandlerFb: (collect: string, flag: number) => void;
|
protected _escHandlerFb: (collect: string, flag: number) => void;
|
||||||
@@ -294,7 +296,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
|||||||
this._errorHandler = null;
|
this._errorHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPrintHandler(callback: (data: string, start: number, end: number) => void): void {
|
setPrintHandler(callback: (data: Uint32Array, start: number, end: number) => void): void {
|
||||||
this._printHandler = callback;
|
this._printHandler = callback;
|
||||||
}
|
}
|
||||||
clearPrintHandler(): void {
|
clearPrintHandler(): void {
|
||||||
@@ -397,7 +399,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
|||||||
this._activeDcsHandler = null;
|
this._activeDcsHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(data: string): void {
|
parse(data: Uint32Array, length: number): void {
|
||||||
let code = 0;
|
let code = 0;
|
||||||
let transition = 0;
|
let transition = 0;
|
||||||
let error = false;
|
let error = false;
|
||||||
@@ -412,15 +414,14 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
|||||||
let callback: Function | null = null;
|
let callback: Function | null = null;
|
||||||
|
|
||||||
// process input string
|
// process input string
|
||||||
const l = data.length;
|
for (let i = 0; i < length; ++i) {
|
||||||
for (let i = 0; i < l; ++i) {
|
code = data[i];
|
||||||
code = data.charCodeAt(i);
|
|
||||||
|
|
||||||
// shortcut for most chars (print action)
|
// shortcut for most chars (print action)
|
||||||
if (currentState === ParserState.GROUND && code > 0x1f && code < 0x80) {
|
if (currentState === ParserState.GROUND && code > 0x1f && code < 0x80) {
|
||||||
print = (~print) ? print : i;
|
print = (~print) ? print : i;
|
||||||
do i++;
|
do i++;
|
||||||
while (i < l && data.charCodeAt(i) > 0x1f && data.charCodeAt(i) < 0x80);
|
while (i < length && data[i] > 0x1f && data[i] < 0x80);
|
||||||
i--;
|
i--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -563,10 +564,10 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
|||||||
break;
|
break;
|
||||||
case ParserAction.OSC_PUT:
|
case ParserAction.OSC_PUT:
|
||||||
for (let j = i + 1; ; j++) {
|
for (let j = i + 1; ; j++) {
|
||||||
if (j >= l
|
if (j >= length
|
||||||
|| (code = data.charCodeAt(j)) < 0x20
|
|| (code = data[j]) < 0x20
|
||||||
|| (code > 0x7f && code <= 0x9f)) {
|
|| (code > 0x7f && code <= 0x9f)) {
|
||||||
osc += data.substring(i, j);
|
osc += utf32ToString(data, i, j);
|
||||||
i = j - 1;
|
i = j - 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -610,9 +611,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
|||||||
|
|
||||||
// push leftover pushable buffers to terminal
|
// push leftover pushable buffers to terminal
|
||||||
if (currentState === ParserState.GROUND && ~print) {
|
if (currentState === ParserState.GROUND && ~print) {
|
||||||
this._printHandler(data, print, data.length);
|
this._printHandler(data, print, length);
|
||||||
} else if (currentState === ParserState.DCS_PASSTHROUGH && ~dcs && dcsHandler) {
|
} else if (currentState === ParserState.DCS_PASSTHROUGH && ~dcs && dcsHandler) {
|
||||||
dcsHandler.put(data, dcs, data.length);
|
dcsHandler.put(data, dcs, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// save non pushable buffers
|
// save non pushable buffers
|
||||||
|
|||||||
@@ -337,7 +337,9 @@ describe('InputHandler', () => {
|
|||||||
it('should not cause an infinite loop (regression test)', () => {
|
it('should not cause an infinite loop (regression test)', () => {
|
||||||
const term = new Terminal();
|
const term = new Terminal();
|
||||||
const inputHandler = new InputHandler(term);
|
const inputHandler = new InputHandler(term);
|
||||||
inputHandler.print(String.fromCharCode(0x200B), 0, 1);
|
const container = new Uint32Array(10);
|
||||||
|
container[0] = 0x200B;
|
||||||
|
inputHandler.print(container, 0, 1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+48
-54
@@ -14,6 +14,8 @@ import { EscapeSequenceParser } from './EscapeSequenceParser';
|
|||||||
import { ICharset } from './core/Types';
|
import { ICharset } from './core/Types';
|
||||||
import { IDisposable } from 'xterm';
|
import { IDisposable } from 'xterm';
|
||||||
import { Disposable } from './common/Lifecycle';
|
import { Disposable } from './common/Lifecycle';
|
||||||
|
import { concat } from './common/TypedArrayUtils';
|
||||||
|
import { StringToUtf32, stringFromCodePoint, utf32ToString } from './core/input/TextDecoder';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map collect to glevel. Used in `selectCharset`.
|
* Map collect to glevel. Used in `selectCharset`.
|
||||||
@@ -32,21 +34,22 @@ const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1,
|
|||||||
* Response: DECRPSS (https://vt100.net/docs/vt510-rm/DECRPSS.html)
|
* Response: DECRPSS (https://vt100.net/docs/vt510-rm/DECRPSS.html)
|
||||||
*/
|
*/
|
||||||
class DECRQSS implements IDcsHandler {
|
class DECRQSS implements IDcsHandler {
|
||||||
private _data: string;
|
private _data: Uint32Array = new Uint32Array(0);
|
||||||
|
|
||||||
constructor(private _terminal: any) { }
|
constructor(private _terminal: any) { }
|
||||||
|
|
||||||
hook(collect: string, params: number[], flag: number): void {
|
hook(collect: string, params: number[], flag: number): void {
|
||||||
// reset data
|
this._data = new Uint32Array(0);
|
||||||
this._data = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
put(data: string, start: number, end: number): void {
|
put(data: Uint32Array, start: number, end: number): void {
|
||||||
this._data += data.substring(start, end);
|
this._data = concat(this._data, data.subarray(start, end));
|
||||||
}
|
}
|
||||||
|
|
||||||
unhook(): void {
|
unhook(): void {
|
||||||
switch (this._data) {
|
const data = utf32ToString(this._data);
|
||||||
|
this._data = new Uint32Array(0);
|
||||||
|
switch (data) {
|
||||||
// valid: DCS 1 $ r Pt ST (xterm)
|
// valid: DCS 1 $ r Pt ST (xterm)
|
||||||
case '"q': // DECSCA
|
case '"q': // DECSCA
|
||||||
return this._terminal.handler(`${C0.ESC}P1$r0"q${C0.ESC}\\`);
|
return this._terminal.handler(`${C0.ESC}P1$r0"q${C0.ESC}\\`);
|
||||||
@@ -66,7 +69,7 @@ class DECRQSS implements IDcsHandler {
|
|||||||
return this._terminal.handler(`${C0.ESC}P1$r${style} q${C0.ESC}\\`);
|
return this._terminal.handler(`${C0.ESC}P1$r${style} q${C0.ESC}\\`);
|
||||||
default:
|
default:
|
||||||
// invalid: DCS 0 $ r Pt ST (xterm)
|
// invalid: DCS 0 $ r Pt ST (xterm)
|
||||||
this._terminal.error('Unknown DCS $q %s', this._data);
|
this._terminal.error('Unknown DCS $q %s', data);
|
||||||
this._terminal.handler(`${C0.ESC}P0$r${C0.ESC}\\`);
|
this._terminal.handler(`${C0.ESC}P0$r${C0.ESC}\\`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,11 +81,17 @@ class DECRQSS implements IDcsHandler {
|
|||||||
* not supported
|
* not supported
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DCS + p Pt ST (xterm)
|
* DCS + q Pt ST (xterm)
|
||||||
* Set Terminfo Data
|
* Request Terminfo String
|
||||||
* not supported
|
* not implemented
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DCS + p Pt ST (xterm)
|
||||||
|
* Set Terminfo Data
|
||||||
|
* not supported
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -94,7 +103,8 @@ class DECRQSS implements IDcsHandler {
|
|||||||
* each function's header comment.
|
* each function's header comment.
|
||||||
*/
|
*/
|
||||||
export class InputHandler extends Disposable implements IInputHandler {
|
export class InputHandler extends Disposable implements IInputHandler {
|
||||||
private _surrogateFirst: string;
|
private _parseBuffer: Uint32Array = new Uint32Array(4096);
|
||||||
|
private _stringDecoder: StringToUtf32 = new StringToUtf32();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected _terminal: IInputHandlingTerminal,
|
protected _terminal: IInputHandlingTerminal,
|
||||||
@@ -104,8 +114,6 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
|
|
||||||
this.register(this._parser);
|
this.register(this._parser);
|
||||||
|
|
||||||
this._surrogateFirst = '';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* custom fallback handlers
|
* custom fallback handlers
|
||||||
*/
|
*/
|
||||||
@@ -290,13 +298,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
this._terminal.log('data: ' + data);
|
this._terminal.log('data: ' + data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply leftover surrogate high from last write
|
if (this._parseBuffer.length < data.length) {
|
||||||
if (this._surrogateFirst) {
|
this._parseBuffer = new Uint32Array(data.length);
|
||||||
data = this._surrogateFirst + data;
|
|
||||||
this._surrogateFirst = '';
|
|
||||||
}
|
}
|
||||||
|
for (let i = 0; i < data.length; ++i) {
|
||||||
this._parser.parse(data);
|
this._parseBuffer[i] = data.charCodeAt(i);
|
||||||
|
}
|
||||||
|
this._parser.parse(this._parseBuffer, this._stringDecoder.decode(data, this._parseBuffer));
|
||||||
|
|
||||||
buffer = this._terminal.buffer;
|
buffer = this._terminal.buffer;
|
||||||
if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
|
if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
|
||||||
@@ -304,9 +312,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public print(data: string, start: number, end: number): void {
|
public print(data: Uint32Array, start: number, end: number): void {
|
||||||
let char: string;
|
|
||||||
let code: number;
|
let code: number;
|
||||||
|
let char: string;
|
||||||
let chWidth: number;
|
let chWidth: number;
|
||||||
const buffer: IBuffer = this._terminal.buffer;
|
const buffer: IBuffer = this._terminal.buffer;
|
||||||
const charset: ICharset = this._terminal.charset;
|
const charset: ICharset = this._terminal.charset;
|
||||||
@@ -318,41 +326,23 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
let bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
|
let bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
|
||||||
|
|
||||||
this._terminal.updateRange(buffer.y);
|
this._terminal.updateRange(buffer.y);
|
||||||
for (let stringPosition = start; stringPosition < end; ++stringPosition) {
|
for (let pos = start; pos < end; ++pos) {
|
||||||
char = data.charAt(stringPosition);
|
code = data[pos];
|
||||||
code = data.charCodeAt(stringPosition);
|
char = stringFromCodePoint(code);
|
||||||
|
|
||||||
// surrogate pair handling
|
|
||||||
if (0xD800 <= code && code <= 0xDBFF) {
|
|
||||||
if (++stringPosition >= end) {
|
|
||||||
// end of input:
|
|
||||||
// handle pairs as true UTF-16 and wait for the second part
|
|
||||||
// since we expect the input comming from a stream there is
|
|
||||||
// a small chance that the surrogate pair got split
|
|
||||||
// therefore we dont process the first char here, instead
|
|
||||||
// it gets added as first char to the next processed chunk
|
|
||||||
this._surrogateFirst = char;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const second = data.charCodeAt(stringPosition);
|
|
||||||
// if the second part is in surrogate pair range create the high codepoint
|
|
||||||
// otherwise fall back to UCS-2 behavior (handle codepoints independently)
|
|
||||||
if (0xDC00 <= second && second <= 0xDFFF) {
|
|
||||||
code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
|
|
||||||
char += data.charAt(stringPosition);
|
|
||||||
} else {
|
|
||||||
stringPosition--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate print space
|
// calculate print space
|
||||||
// expensive call, therefore we save width in line buffer
|
// expensive call, therefore we save width in line buffer
|
||||||
chWidth = wcwidth(code);
|
chWidth = wcwidth(code);
|
||||||
|
|
||||||
// get charset replacement character
|
// get charset replacement character
|
||||||
if (charset) {
|
// charset are only defined for ASCII, therefore we only
|
||||||
char = charset[char] || char;
|
// search for an replacement char if code < 127
|
||||||
code = char.charCodeAt(0);
|
if (code < 127 && charset) {
|
||||||
|
const ch = charset[char];
|
||||||
|
if (ch) {
|
||||||
|
code = ch.charCodeAt(0);
|
||||||
|
char = ch;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (screenReaderMode) {
|
if (screenReaderMode) {
|
||||||
@@ -1294,7 +1284,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
if (this._terminal.element) {
|
if (this._terminal.element) {
|
||||||
this._terminal.element.classList.add('enable-mouse-events');
|
this._terminal.element.classList.add('enable-mouse-events');
|
||||||
}
|
}
|
||||||
this._terminal.selectionManager.disable();
|
if (this._terminal.selectionManager) {
|
||||||
|
this._terminal.selectionManager.disable();
|
||||||
|
}
|
||||||
this._terminal.log('Binding to mouse events.');
|
this._terminal.log('Binding to mouse events.');
|
||||||
break;
|
break;
|
||||||
case 1004: // send focusin/focusout events
|
case 1004: // send focusin/focusout events
|
||||||
@@ -1484,7 +1476,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
if (this._terminal.element) {
|
if (this._terminal.element) {
|
||||||
this._terminal.element.classList.remove('enable-mouse-events');
|
this._terminal.element.classList.remove('enable-mouse-events');
|
||||||
}
|
}
|
||||||
this._terminal.selectionManager.enable();
|
if (this._terminal.selectionManager) {
|
||||||
|
this._terminal.selectionManager.enable();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 1004: // send focusin/focusout events
|
case 1004: // send focusin/focusout events
|
||||||
this._terminal.sendFocus = false;
|
this._terminal.sendFocus = false;
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ describe('Linkifier', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
terminal = new MockTerminal();
|
terminal = new MockTerminal();
|
||||||
terminal.cols = 100;
|
(terminal as any).cols = 100;
|
||||||
terminal.rows = 10;
|
(terminal as any).rows = 10;
|
||||||
terminal.buffer = new MockBuffer();
|
terminal.buffer = new MockBuffer();
|
||||||
(<MockBuffer>terminal.buffer).setLines(new CircularList<IBufferLine>(20));
|
(<MockBuffer>terminal.buffer).setLines(new CircularList<IBufferLine>(20));
|
||||||
terminal.buffer.ydisp = 0;
|
terminal.buffer.ydisp = 0;
|
||||||
@@ -65,7 +65,7 @@ describe('Linkifier', () => {
|
|||||||
function assertLinkifiesRow(rowText: string, linkMatcherRegex: RegExp, links: {x: number, length: number}[], done: MochaDone): void {
|
function assertLinkifiesRow(rowText: string, linkMatcherRegex: RegExp, links: {x: number, length: number}[], done: MochaDone): void {
|
||||||
addRow(rowText);
|
addRow(rowText);
|
||||||
linkifier.registerLinkMatcher(linkMatcherRegex, () => {});
|
linkifier.registerLinkMatcher(linkMatcherRegex, () => {});
|
||||||
terminal.rows = terminal.buffer.lines.length - 1;
|
(terminal as any).rows = terminal.buffer.lines.length - 1;
|
||||||
linkifier.linkifyRows();
|
linkifier.linkifyRows();
|
||||||
// Allow linkify to happen
|
// Allow linkify to happen
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -142,19 +142,19 @@ describe('Linkifier', () => {
|
|||||||
});
|
});
|
||||||
describe('multi-line links', () => {
|
describe('multi-line links', () => {
|
||||||
it('should match links that start on line 1/2 of a wrapped line and end on the last character of line 1/2', done => {
|
it('should match links that start on line 1/2 of a wrapped line and end on the last character of line 1/2', done => {
|
||||||
terminal.cols = 4;
|
(terminal as any).cols = 4;
|
||||||
assertLinkifiesMultiLineLink('12345', /1234/, [{x1: 0, x2: 4, y1: 0, y2: 0}], done);
|
assertLinkifiesMultiLineLink('12345', /1234/, [{x1: 0, x2: 4, y1: 0, y2: 0}], done);
|
||||||
});
|
});
|
||||||
it('should match links that start on line 1/2 of a wrapped line and wrap to line 2/2', done => {
|
it('should match links that start on line 1/2 of a wrapped line and wrap to line 2/2', done => {
|
||||||
terminal.cols = 4;
|
(terminal as any).cols = 4;
|
||||||
assertLinkifiesMultiLineLink('12345', /12345/, [{x1: 0, x2: 1, y1: 0, y2: 1}], done);
|
assertLinkifiesMultiLineLink('12345', /12345/, [{x1: 0, x2: 1, y1: 0, y2: 1}], done);
|
||||||
});
|
});
|
||||||
it('should match links that start and end on line 2/2 of a wrapped line', done => {
|
it('should match links that start and end on line 2/2 of a wrapped line', done => {
|
||||||
terminal.cols = 4;
|
(terminal as any).cols = 4;
|
||||||
assertLinkifiesMultiLineLink('12345678', /5678/, [{x1: 0, x2: 4, y1: 1, y2: 1}], done);
|
assertLinkifiesMultiLineLink('12345678', /5678/, [{x1: 0, x2: 4, y1: 1, y2: 1}], done);
|
||||||
});
|
});
|
||||||
it('should match links that start on line 2/3 of a wrapped line and wrap to line 3/3', done => {
|
it('should match links that start on line 2/3 of a wrapped line and wrap to line 3/3', done => {
|
||||||
terminal.cols = 4;
|
(terminal as any).cols = 4;
|
||||||
assertLinkifiesMultiLineLink('123456789', /56789/, [{x1: 0, x2: 1, y1: 1, y2: 2}], done);
|
assertLinkifiesMultiLineLink('123456789', /56789/, [{x1: 0, x2: 1, y1: 1, y2: 2}], done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ describe('SelectionManager', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
terminal = new TestMockTerminal();
|
terminal = new TestMockTerminal();
|
||||||
terminal.cols = 80;
|
(terminal as any).cols = 80;
|
||||||
terminal.rows = 2;
|
(terminal as any).rows = 2;
|
||||||
terminal.options.scrollback = 100;
|
terminal.options.scrollback = 100;
|
||||||
terminal.buffers = new BufferSet(terminal);
|
terminal.buffers = new BufferSet(terminal);
|
||||||
terminal.buffer = terminal.buffers.active;
|
terminal.buffer = terminal.buffers.active;
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ describe('SelectionManager', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
terminal = new MockTerminal();
|
terminal = new MockTerminal();
|
||||||
terminal.cols = 80;
|
(terminal as any).cols = 80;
|
||||||
terminal.rows = 2;
|
(terminal as any).rows = 2;
|
||||||
terminal.options.scrollback = 10;
|
terminal.options.scrollback = 10;
|
||||||
terminal.buffers = new BufferSet(terminal);
|
terminal.buffers = new BufferSet(terminal);
|
||||||
terminal.buffer = terminal.buffers.active;
|
terminal.buffer = terminal.buffers.active;
|
||||||
|
|||||||
+8
-3
@@ -568,12 +568,12 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
// Firefox doesn't appear to fire the contextmenu event on right click
|
// Firefox doesn't appear to fire the contextmenu event on right click
|
||||||
this.register(addDisposableDomListener(this.element, 'mousedown', (event: MouseEvent) => {
|
this.register(addDisposableDomListener(this.element, 'mousedown', (event: MouseEvent) => {
|
||||||
if (event.button === 2) {
|
if (event.button === 2) {
|
||||||
rightClickHandler(event, this.textarea, this.selectionManager, this.options.rightClickSelectsWord);
|
rightClickHandler(event, this, this.selectionManager, this.options.rightClickSelectsWord);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
this.register(addDisposableDomListener(this.element, 'contextmenu', (event: MouseEvent) => {
|
this.register(addDisposableDomListener(this.element, 'contextmenu', (event: MouseEvent) => {
|
||||||
rightClickHandler(event, this.textarea, this.selectionManager, this.options.rightClickSelectsWord);
|
rightClickHandler(event, this, this.selectionManager, this.options.rightClickSelectsWord);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,7 +585,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
// that the regular click event doesn't fire for the middle mouse button.
|
// that the regular click event doesn't fire for the middle mouse button.
|
||||||
this.register(addDisposableDomListener(this.element, 'auxclick', (event: MouseEvent) => {
|
this.register(addDisposableDomListener(this.element, 'auxclick', (event: MouseEvent) => {
|
||||||
if (event.button === 1) {
|
if (event.button === 1) {
|
||||||
moveTextAreaUnderMouseCursor(event, this.textarea);
|
moveTextAreaUnderMouseCursor(event, this);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -740,6 +740,11 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
|||||||
this.mouseHelper = new MouseHelper(this.renderer);
|
this.mouseHelper = new MouseHelper(this.renderer);
|
||||||
// apply mouse event classes set by escape codes before terminal was attached
|
// apply mouse event classes set by escape codes before terminal was attached
|
||||||
this.element.classList.toggle('enable-mouse-events', this.mouseEvents);
|
this.element.classList.toggle('enable-mouse-events', this.mouseEvents);
|
||||||
|
if (this.mouseEvents) {
|
||||||
|
this.selectionManager.disable();
|
||||||
|
} else {
|
||||||
|
this.selectionManager.enable();
|
||||||
|
}
|
||||||
|
|
||||||
if (this.options.screenReaderMode) {
|
if (this.options.screenReaderMode) {
|
||||||
// Note that this must be done *after* the renderer is created in order to
|
// Note that this must be done *after* the renderer is created in order to
|
||||||
|
|||||||
+17
-9
@@ -111,7 +111,7 @@ export interface ICompositionHelper {
|
|||||||
*/
|
*/
|
||||||
export interface IInputHandler {
|
export interface IInputHandler {
|
||||||
parse(data: string): void;
|
parse(data: string): void;
|
||||||
print(data: string, start: number, end: number): void;
|
print(data: Uint32Array, start: number, end: number): void;
|
||||||
|
|
||||||
/** C0 BEL */ bell(): void;
|
/** C0 BEL */ bell(): void;
|
||||||
/** C0 LF */ lineFeed(): void;
|
/** C0 LF */ lineFeed(): void;
|
||||||
@@ -237,7 +237,7 @@ export interface IBufferAccessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IElementAccessor {
|
export interface IElementAccessor {
|
||||||
element: HTMLElement;
|
readonly element: HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILinkifierAccessor {
|
export interface ILinkifierAccessor {
|
||||||
@@ -452,18 +452,26 @@ export interface IParsingState {
|
|||||||
* DCS handler signature for EscapeSequenceParser.
|
* DCS handler signature for EscapeSequenceParser.
|
||||||
* EscapeSequenceParser handles DCS commands via separate
|
* EscapeSequenceParser handles DCS commands via separate
|
||||||
* subparsers that get hook/unhooked and can handle
|
* subparsers that get hook/unhooked and can handle
|
||||||
* arbitrary amount of print data.
|
* arbitrary amount of data.
|
||||||
|
*
|
||||||
* On entering a DSC sequence `hook` is called by
|
* On entering a DSC sequence `hook` is called by
|
||||||
* `EscapeSequenceParser`. Use it to initialize or reset
|
* `EscapeSequenceParser`. Use it to initialize or reset
|
||||||
* states needed to handle the current DCS sequence.
|
* states needed to handle the current DCS sequence.
|
||||||
|
* Note: A DCS parser is only instantiated once, therefore
|
||||||
|
* you cannot rely on the ctor to reinitialize state.
|
||||||
|
*
|
||||||
* EscapeSequenceParser will call `put` several times if the
|
* EscapeSequenceParser will call `put` several times if the
|
||||||
* parsed string got splitted, therefore you might have to collect
|
* parsed data got split, therefore you might have to collect
|
||||||
* `data` until `unhook` is called. `unhook` marks the end
|
* `data` until `unhook` is called.
|
||||||
* of the current DCS sequence.
|
* Note: `data` is borrowed, if you cannot process the data
|
||||||
|
* in chunks you have to copy it, doing otherwise will lead to
|
||||||
|
* data losses or corruption.
|
||||||
|
*
|
||||||
|
* `unhook` marks the end of the current DCS sequence.
|
||||||
*/
|
*/
|
||||||
export interface IDcsHandler {
|
export interface IDcsHandler {
|
||||||
hook(collect: string, params: number[], flag: number): void;
|
hook(collect: string, params: number[], flag: number): void;
|
||||||
put(data: string, start: number, end: number): void;
|
put(data: Uint32Array, start: number, end: number): void;
|
||||||
unhook(): void;
|
unhook(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,9 +488,9 @@ export interface IEscapeSequenceParser extends IDisposable {
|
|||||||
* Parse string `data`.
|
* Parse string `data`.
|
||||||
* @param data The data to parse.
|
* @param data The data to parse.
|
||||||
*/
|
*/
|
||||||
parse(data: string): void;
|
parse(data: Uint32Array, length: number): void;
|
||||||
|
|
||||||
setPrintHandler(callback: (data: string, start: number, end: number) => void): void;
|
setPrintHandler(callback: (data: Uint32Array, start: number, end: number) => void): void;
|
||||||
clearPrintHandler(): void;
|
clearPrintHandler(): void;
|
||||||
|
|
||||||
setExecuteHandler(flag: string, callback: () => void): void;
|
setExecuteHandler(flag: string, callback: () => void): void;
|
||||||
|
|||||||
+15
-17
@@ -154,24 +154,22 @@ export class CircularList<T> extends EventEmitter implements ICircularList<T> {
|
|||||||
this._length -= deleteCount;
|
this._length -= deleteCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items && items.length) {
|
// Add items
|
||||||
// Add items
|
for (let i = this._length - 1; i >= start; i--) {
|
||||||
for (let i = this._length - 1; i >= start; i--) {
|
this._array[this._getCyclicIndex(i + items.length)] = this._array[this._getCyclicIndex(i)];
|
||||||
this._array[this._getCyclicIndex(i + items.length)] = this._array[this._getCyclicIndex(i)];
|
}
|
||||||
}
|
for (let i = 0; i < items.length; i++) {
|
||||||
for (let i = 0; i < items.length; i++) {
|
this._array[this._getCyclicIndex(start + i)] = items[i];
|
||||||
this._array[this._getCyclicIndex(start + i)] = items[i];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust length as needed
|
// Adjust length as needed
|
||||||
if (this._length + items.length > this._maxLength) {
|
if (this._length + items.length > this._maxLength) {
|
||||||
const countToTrim = (this._length + items.length) - this._maxLength;
|
const countToTrim = (this._length + items.length) - this._maxLength;
|
||||||
this._startIndex += countToTrim;
|
this._startIndex += countToTrim;
|
||||||
this._length = this._maxLength;
|
this._length = this._maxLength;
|
||||||
this.emitMayRemoveListeners('trim', countToTrim);
|
this.emitMayRemoveListeners('trim', countToTrim);
|
||||||
} else {
|
} else {
|
||||||
this._length += items.length;
|
this._length += items.length;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,21 +3,20 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { fillFallback } from './TypedArrayUtils';
|
import { fillFallback, concat } from './TypedArrayUtils';
|
||||||
|
|
||||||
type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray
|
type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray
|
||||||
| Int8Array | Int16Array | Int32Array
|
| Int8Array | Int16Array | Int32Array
|
||||||
| Float32Array | Float64Array;
|
| Float32Array | Float64Array;
|
||||||
|
|
||||||
describe('polyfill conformance tests', function(): void {
|
function deepEquals(a: TypedArray, b: TypedArray): void {
|
||||||
|
assert.equal(a.length, b.length);
|
||||||
function deepEquals(a: TypedArray, b: TypedArray): void {
|
for (let i = 0; i < a.length; ++i) {
|
||||||
assert.equal(a.length, b.length);
|
assert.equal(a[i], b[i]);
|
||||||
for (let i = 0; i < a.length; ++i) {
|
|
||||||
assert.equal(a[i], b[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('polyfill conformance tests', function(): void {
|
||||||
describe('TypedArray.fill', function(): void {
|
describe('TypedArray.fill', function(): void {
|
||||||
it('should work with all typed array types', function(): void {
|
it('should work with all typed array types', function(): void {
|
||||||
const u81 = new Uint8Array(5);
|
const u81 = new Uint8Array(5);
|
||||||
@@ -87,3 +86,12 @@ describe('polyfill conformance tests', function(): void {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('typed array convenience functions', () => {
|
||||||
|
it('concat', () => {
|
||||||
|
const a = new Uint8Array([1, 2, 3, 4, 5]);
|
||||||
|
const b = new Uint8Array([6, 7, 8, 9, 0]);
|
||||||
|
const merged = concat(a, b);
|
||||||
|
deepEquals(merged, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -3,15 +3,15 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray
|
||||||
|
| Int8Array | Int16Array | Int32Array
|
||||||
|
| Float32Array | Float64Array;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* polyfill for TypedArray.fill
|
* polyfill for TypedArray.fill
|
||||||
* This is needed to support .fill in all safari versions and IE 11.
|
* This is needed to support .fill in all safari versions and IE 11.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray
|
|
||||||
| Int8Array | Int16Array | Int32Array
|
|
||||||
| Float32Array | Float64Array;
|
|
||||||
|
|
||||||
export function fill<T extends TypedArray>(array: T, value: number, start?: number, end?: number): T {
|
export function fill<T extends TypedArray>(array: T, value: number, start?: number, end?: number): T {
|
||||||
// all modern engines that support .fill
|
// all modern engines that support .fill
|
||||||
if (array.fill) {
|
if (array.fill) {
|
||||||
@@ -39,3 +39,14 @@ export function fillFallback<T extends TypedArray>(array: T, value: number, star
|
|||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concat two typed arrays `a` and `b`.
|
||||||
|
* Returns a new typed array.
|
||||||
|
*/
|
||||||
|
export function concat<T extends TypedArray>(a: T, b: T): T {
|
||||||
|
const result = new (a.constructor as any)(a.length + b.length);
|
||||||
|
result.set(a);
|
||||||
|
result.set(b, a.length);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { assert } from 'chai';
|
||||||
|
import { StringToUtf32, stringFromCodePoint, utf32ToString } from './TextDecoder';
|
||||||
|
|
||||||
|
describe('text encodings', () => {
|
||||||
|
it('stringFromCodePoint/utf32ToString', () => {
|
||||||
|
const s = 'abcdefg';
|
||||||
|
const data = new Uint32Array(s.length);
|
||||||
|
for (let i = 0; i < s.length; ++i) {
|
||||||
|
data[i] = s.charCodeAt(i);
|
||||||
|
assert.equal(stringFromCodePoint(data[i]), s[i]);
|
||||||
|
}
|
||||||
|
assert.equal(utf32ToString(data), s);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('StringToUtf32 Decoder', () => {
|
||||||
|
describe('full codepoint test', () => {
|
||||||
|
it('0..65535', () => {
|
||||||
|
const decoder = new StringToUtf32();
|
||||||
|
const target = new Uint32Array(5);
|
||||||
|
for (let i = 0; i < 65536; ++i) {
|
||||||
|
// skip surrogate pairs
|
||||||
|
if (i >= 0xD800 && i <= 0xDFFF) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const length = decoder.decode(String.fromCharCode(i), target);
|
||||||
|
assert.equal(length, 1);
|
||||||
|
assert.equal(target[0], i);
|
||||||
|
assert.equal(utf32ToString(target, 0, length), String.fromCharCode(i));
|
||||||
|
decoder.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('65536..0x10FFFF (surrogates)', function(): void {
|
||||||
|
this.timeout(20000);
|
||||||
|
const decoder = new StringToUtf32();
|
||||||
|
const target = new Uint32Array(5);
|
||||||
|
for (let i = 65536; i < 0x10FFFF; ++i) {
|
||||||
|
const codePoint = i - 0x10000;
|
||||||
|
const s = String.fromCharCode((codePoint >> 10) + 0xD800) + String.fromCharCode((codePoint % 0x400) + 0xDC00);
|
||||||
|
const length = decoder.decode(s, target);
|
||||||
|
assert.equal(length, 1);
|
||||||
|
assert.equal(target[0], i);
|
||||||
|
assert.equal(utf32ToString(target, 0, length), s);
|
||||||
|
decoder.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('stream handling', () => {
|
||||||
|
it('surrogates mixed advance by 1', () => {
|
||||||
|
const decoder = new StringToUtf32();
|
||||||
|
const target = new Uint32Array(5);
|
||||||
|
const input = 'Ä€𝄞Ö𝄞€Ü𝄞€';
|
||||||
|
let decoded = '';
|
||||||
|
for (let i = 0; i < input.length; ++i) {
|
||||||
|
const written = decoder.decode(input[i], target);
|
||||||
|
decoded += utf32ToString(target, written);
|
||||||
|
}
|
||||||
|
assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StringToUtf32 - decodes UTF16 sequences into UTF32 codepoints.
|
||||||
|
* To keep the decoder in line with JS strings it handles single surrogates as UCS2.
|
||||||
|
*/
|
||||||
|
export class StringToUtf32 {
|
||||||
|
private _interim: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears interim and resets decoder to clean state.
|
||||||
|
*/
|
||||||
|
public clear(): void {
|
||||||
|
this._interim = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode JS string to UTF32 codepoints.
|
||||||
|
* The methods assumes stream input and will store partly transmitted
|
||||||
|
* surrogate pairs and decode them with the next data chunk.
|
||||||
|
* Note: The method does no bound checks for target, therefore make sure
|
||||||
|
* the provided input data does not exceed the size of `target`.
|
||||||
|
* Returns the number of written codepoints in `target`.
|
||||||
|
*/
|
||||||
|
decode(input: string, target: Uint32Array): number {
|
||||||
|
const length = input.length;
|
||||||
|
|
||||||
|
if (!length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = 0;
|
||||||
|
let startPos = 0;
|
||||||
|
|
||||||
|
// handle leftover surrogate high
|
||||||
|
if (this._interim) {
|
||||||
|
const second = input.charCodeAt(startPos++);
|
||||||
|
if (0xDC00 <= second && second <= 0xDFFF) {
|
||||||
|
target[size++] = (this._interim - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
|
||||||
|
} else {
|
||||||
|
// illegal codepoint (USC2 handling)
|
||||||
|
target[size++] = this._interim;
|
||||||
|
target[size++] = second;
|
||||||
|
}
|
||||||
|
this._interim = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = startPos; i < length; ++i) {
|
||||||
|
const code = input.charCodeAt(i);
|
||||||
|
// surrogate pair first
|
||||||
|
if (0xD800 <= code && code <= 0xDBFF) {
|
||||||
|
if (++i >= length) {
|
||||||
|
this._interim = code;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
const second = input.charCodeAt(i);
|
||||||
|
if (0xDC00 <= second && second <= 0xDFFF) {
|
||||||
|
target[size++] = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
|
||||||
|
} else {
|
||||||
|
// illegal codepoint (USC2 handling)
|
||||||
|
target[size++] = code;
|
||||||
|
target[size++] = second;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
target[size++] = code;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert UTF32 codepoint into JS string.
|
||||||
|
*/
|
||||||
|
export function stringFromCodePoint(codePoint: number): string {
|
||||||
|
if (codePoint > 0xFFFF) {
|
||||||
|
// UTF32 to UTF16 conversion (see comments in utf32ToString)
|
||||||
|
codePoint -= 0x10000;
|
||||||
|
return String.fromCharCode((codePoint >> 10) + 0xD800) + String.fromCharCode((codePoint % 0x400) + 0xDC00);
|
||||||
|
}
|
||||||
|
return String.fromCharCode(codePoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert UTF32 char codes into JS string.
|
||||||
|
* Basically the same as `stringFromCodePoint` but for multiple codepoints
|
||||||
|
* in a loop (which is a lot faster).
|
||||||
|
*/
|
||||||
|
export function utf32ToString(data: Uint32Array, start: number = 0, end: number = data.length): string {
|
||||||
|
let result = '';
|
||||||
|
for (let i = start; i < end; ++i) {
|
||||||
|
let codepoint = data[i];
|
||||||
|
if (codepoint > 0xFFFF) {
|
||||||
|
// JS string are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate pair
|
||||||
|
// conversion rules:
|
||||||
|
// - subtract 0x10000 from code point, leaving a 20 bit number
|
||||||
|
// - add high 10 bits to 0xD800 --> first surrogate
|
||||||
|
// - add low 10 bits to 0xDC00 --> second surrogate
|
||||||
|
codepoint -= 0x10000;
|
||||||
|
result += String.fromCharCode((codepoint >> 10) + 0xD800) + String.fromCharCode((codepoint % 0x400) + 0xDC00);
|
||||||
|
} else {
|
||||||
|
result += String.fromCharCode(codepoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -70,7 +70,7 @@ export class Renderer extends EventEmitter implements IRenderer {
|
|||||||
// Detect whether IntersectionObserver is detected and enable renderer pause
|
// Detect whether IntersectionObserver is detected and enable renderer pause
|
||||||
// and resume based on terminal visibility if so
|
// and resume based on terminal visibility if so
|
||||||
if ('IntersectionObserver' in window) {
|
if ('IntersectionObserver' in window) {
|
||||||
const observer = new IntersectionObserver(e => this.onIntersectionChange(e[0]), { threshold: 0 });
|
const observer = new IntersectionObserver(e => this.onIntersectionChange(e[e.length - 1]), { threshold: 0 });
|
||||||
observer.observe(this._terminal.element);
|
observer.observe(this._terminal.element);
|
||||||
this.register({ dispose: () => observer.disconnect() });
|
this.register({ dispose: () => observer.disconnect() });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export class SelectionRenderLayer extends BaseRenderLayer {
|
|||||||
|
|
||||||
// Selection does not exist
|
// Selection does not exist
|
||||||
if (!start || !end) {
|
if (!start || !end) {
|
||||||
|
this._clearState();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+25
-19
@@ -85,26 +85,32 @@ export function pasteHandler(ev: ClipboardEvent, term: ITerminal): void {
|
|||||||
* @param ev The original right click event to be handled.
|
* @param ev The original right click event to be handled.
|
||||||
* @param textarea The terminal's textarea.
|
* @param textarea The terminal's textarea.
|
||||||
*/
|
*/
|
||||||
export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextAreaElement): void {
|
export function moveTextAreaUnderMouseCursor(ev: MouseEvent, term: ITerminal): void {
|
||||||
// Bring textarea at the cursor position
|
|
||||||
textarea.style.position = 'fixed';
|
|
||||||
textarea.style.width = '20px';
|
|
||||||
textarea.style.height = '20px';
|
|
||||||
textarea.style.left = (ev.clientX - 10) + 'px';
|
|
||||||
textarea.style.top = (ev.clientY - 10) + 'px';
|
|
||||||
textarea.style.zIndex = '1000';
|
|
||||||
|
|
||||||
textarea.focus();
|
// Calculate textarea position relative to the screen element
|
||||||
|
const pos = term.screenElement.getBoundingClientRect();
|
||||||
|
const left = ev.clientX - pos.left - 10;
|
||||||
|
const top = ev.clientY - pos.top - 10;
|
||||||
|
|
||||||
|
// Bring textarea at the cursor position
|
||||||
|
term.textarea.style.position = 'absolute';
|
||||||
|
term.textarea.style.width = '20px';
|
||||||
|
term.textarea.style.height = '20px';
|
||||||
|
term.textarea.style.left = `${left}px`;
|
||||||
|
term.textarea.style.top = `${top}px`;
|
||||||
|
term.textarea.style.zIndex = '1000';
|
||||||
|
|
||||||
|
term.textarea.focus();
|
||||||
|
|
||||||
// Reset the terminal textarea's styling
|
// Reset the terminal textarea's styling
|
||||||
// Timeout needs to be long enough for click event to be handled.
|
// Timeout needs to be long enough for click event to be handled.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
textarea.style.position = null;
|
term.textarea.style.position = null;
|
||||||
textarea.style.width = null;
|
term.textarea.style.width = null;
|
||||||
textarea.style.height = null;
|
term.textarea.style.height = null;
|
||||||
textarea.style.left = null;
|
term.textarea.style.left = null;
|
||||||
textarea.style.top = null;
|
term.textarea.style.top = null;
|
||||||
textarea.style.zIndex = null;
|
term.textarea.style.zIndex = null;
|
||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,14 +121,14 @@ export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextA
|
|||||||
* @param selectionManager The terminal's selection manager.
|
* @param selectionManager The terminal's selection manager.
|
||||||
* @param shouldSelectWord If true and there is no selection the current word will be selected
|
* @param shouldSelectWord If true and there is no selection the current word will be selected
|
||||||
*/
|
*/
|
||||||
export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager, shouldSelectWord: boolean): void {
|
export function rightClickHandler(ev: MouseEvent, term: ITerminal, selectionManager: ISelectionManager, shouldSelectWord: boolean): void {
|
||||||
moveTextAreaUnderMouseCursor(ev, textarea);
|
moveTextAreaUnderMouseCursor(ev, term);
|
||||||
|
|
||||||
if (shouldSelectWord && !selectionManager.isClickInSelection(ev)) {
|
if (shouldSelectWord && !selectionManager.isClickInSelection(ev)) {
|
||||||
selectionManager.selectWordAtCursor(ev);
|
selectionManager.selectWordAtCursor(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get textarea ready to copy from the context menu
|
// Get textarea ready to copy from the context menu
|
||||||
textarea.value = selectionManager.selectionText;
|
term.textarea.value = selectionManager.selectionText;
|
||||||
textarea.select();
|
term.textarea.select();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.xterm {
|
.xterm {
|
||||||
font-family: courier-new, courier, monospace;
|
|
||||||
font-feature-settings: "liga" 0;
|
font-feature-settings: "liga" 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|||||||
Vendored
+14
-8
@@ -314,28 +314,32 @@ declare module 'xterm' {
|
|||||||
/**
|
/**
|
||||||
* The element containing the terminal.
|
* The element containing the terminal.
|
||||||
*/
|
*/
|
||||||
element: HTMLElement;
|
readonly element: HTMLElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The textarea that accepts input for the terminal.
|
* The textarea that accepts input for the terminal.
|
||||||
*/
|
*/
|
||||||
textarea: HTMLTextAreaElement;
|
readonly textarea: HTMLTextAreaElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of rows in the terminal's viewport.
|
* The number of rows in the terminal's viewport. Use
|
||||||
|
* `ITerminalOptions.rows` to set this in the constructor and
|
||||||
|
* `Terminal.resize` for when the terminal exists.
|
||||||
*/
|
*/
|
||||||
rows: number;
|
readonly rows: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of columns in the terminal's viewport.
|
* The number of columns in the terminal's viewport. Use
|
||||||
|
* `ITerminalOptions.cols` to set this in the constructor and
|
||||||
|
* `Terminal.resize` for when the terminal exists.
|
||||||
*/
|
*/
|
||||||
cols: number;
|
readonly cols: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (EXPERIMENTAL) Get all markers registered against the buffer. If the alt
|
* (EXPERIMENTAL) Get all markers registered against the buffer. If the alt
|
||||||
* buffer is active this will always return [].
|
* buffer is active this will always return [].
|
||||||
*/
|
*/
|
||||||
markers: IMarker[];
|
readonly markers: IMarker[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Natural language strings that can be localized.
|
* Natural language strings that can be localized.
|
||||||
@@ -439,7 +443,9 @@ declare module 'xterm' {
|
|||||||
addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable;
|
addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resizes the terminal.
|
* Resizes the terminal. It's best practice to debounce calls to resize,
|
||||||
|
* this will help ensure that the pty can respond to the resize event
|
||||||
|
* before another one occurs.
|
||||||
* @param x The number of columns to resize to.
|
* @param x The number of columns to resize to.
|
||||||
* @param y The number of rows to resize to.
|
* @param y The number of rows to resize to.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user