mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into typedarray_parser
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
---
|
||||
name: Question
|
||||
about: The issue tracker is not for questions. Please ask questions on https://stackoverflow.com/questions/tagged/xtermjs or https://gitter.im/sourcelair/xterm.js.
|
||||
about: The issue tracker is not for questions. Please ask questions on https://stackoverflow.com/questions/tagged/xtermjs
|
||||
---
|
||||
|
||||
🛑 The issue tracker is not for questions 🛑
|
||||
|
||||
If you have a question, please ask it on https://stackoverflow.com/questions/tagged/xtermjs or https://gitter.im/sourcelair/xterm.js.
|
||||
If you have a question, please ask it on https://stackoverflow.com/questions/tagged/xtermjs.
|
||||
|
||||
@@ -12,7 +12,6 @@ npm-debug.log
|
||||
/.idea/
|
||||
.env
|
||||
build/
|
||||
.vscode/
|
||||
.DS_Store
|
||||
fixtures/typings-test/*.js
|
||||
package-lock.json
|
||||
|
||||
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Unit Tests",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/mocha",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/mocha.cmd"
|
||||
},
|
||||
"runtimeArgs": [
|
||||
"--colors",
|
||||
"--recursive",
|
||||
"${workspaceRoot}/lib"
|
||||
],
|
||||
"sourceMaps": true,
|
||||
"outFiles": [ "${workspaceRoot}/lib/**/*.js" ],
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
},
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Demo Client",
|
||||
"url": "http://0.0.0.0:3000",
|
||||
"windows": {
|
||||
"url": "http://127.0.0.1:3000"
|
||||
},
|
||||
"webRoot": "${workspaceFolder}/"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Demo Server",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": [
|
||||
"run",
|
||||
"start-debug"
|
||||
],
|
||||
"port": 9229
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2,23 +2,20 @@
|
||||
|
||||
[](https://dev.azure.com/xtermjs/xterm.js/_build/latest?definitionId=3)
|
||||
[](https://coveralls.io/github/xtermjs/xterm.js?branch=master)
|
||||
[](https://gitter.im/sourcelair/xterm.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
[](https://www.jsdelivr.com/package/npm/xterm)
|
||||
|
||||
Xterm.js is a terminal front-end component written in JavaScript that works in the browser.
|
||||
|
||||
It enables applications to provide fully featured terminals to their users and create great development experiences.
|
||||
Xterm.js is a front-end component written in TypeScript that lets applications bring fully-featured terminals to their users in the browser. It's used by popular projects such as VS Code, Hyper and Theia.
|
||||
|
||||
## Features
|
||||
- **Text-based application support**: Use xterm.js to work with applications like `bash`, `git` etc.
|
||||
- **Curses-based application support**: Use xterm.js to work with applications like `vim`, `tmux` etc.
|
||||
- **Mouse events support**: Xterm.js captures mouse events like click and scroll and passes them to the terminal's back-end controlling process
|
||||
- **CJK (Chinese, Japanese, Korean) character support**: Xterm.js renders CJK characters seamlessly
|
||||
- **IME support**: Insert international (including CJK) characters using IME input with your keyboard
|
||||
- **Self-contained library**: Xterm.js works on its own. It does not require any external libraries like jQuery or React to work
|
||||
- **Modular, event-based API**: Lets you build addons and themes with ease
|
||||
|
||||
- **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
|
||||
- **Rich unicode support**: Supports CJK, emojis and IMEs
|
||||
- **Self-contained**: Requires zero dependencies to work
|
||||
- **Accessible**: Screen reader support can be turned on using the `screenReaderMode` option
|
||||
- **And much more**: Links, theming, addons, well documented API, etc.
|
||||
|
||||
## 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 `bash`. Xterm.js can be connected to processes like `bash` and let you interact with them (provide input, receive output)
|
||||
|
||||
@@ -30,7 +27,7 @@ First you need to install the module, we ship exclusively through [npm](https://
|
||||
npm install xterm
|
||||
```
|
||||
|
||||
To start using xterm.js on your browser, add the `xterm.js` and `xterm.css` to the head of your html page. Then create a `<div id="terminal"></div>` onto which xterm can attach itself.
|
||||
To start using xterm.js on your browser, add the `xterm.js` and `xterm.css` to the head of your html page. Then create a `<div id="terminal"></div>` onto which xterm can attach itself. Finally instantiate the `Terminal` object and then call the `open` function with the DOM object of the `div`.
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
@@ -50,33 +47,24 @@ To start using xterm.js on your browser, add the `xterm.js` and `xterm.css` to t
|
||||
</html>
|
||||
```
|
||||
|
||||
Finally instantiate the `Terminal` object and then call the `open` function with the DOM object of the `div`.
|
||||
|
||||
### Importing
|
||||
|
||||
The proposed way to load xterm.js is via the ES6 module syntax.
|
||||
The recommended way to load xterm.js is via the ES6 module syntax:
|
||||
|
||||
```javascript
|
||||
import { Terminal } from 'xterm';
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
The full API for xterm.js is contained within the [TypeScript declaration file](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts), use the branch/tag picker in GitHub (`w`) to navigate to the correct version of the API.
|
||||
|
||||
Note that some APIs are marked *experimental*, these are added so we can experiment with new ideas without committing to support it like a normal semver API. Note that these APIs can change radically between versions so be sure to read release notes if you plan on using experimental APIs.
|
||||
|
||||
### Addons
|
||||
|
||||
Addons are JavaScript modules that extend the `Terminal` prototype with new methods and attributes to provide additional functionality. There are a handful available in the main repository in the `src/addons` directory and you can even write your own, by using xterm.js' public API.
|
||||
Addons are JavaScript modules that extend the `Terminal` prototype with new methods and attributes to provide additional functionality. There are a handful available in the main repository in the `src/addons` directory and you can even write your own by using the [public API](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts).
|
||||
|
||||
To use an addon, just import the JavaScript module and pass it to `Terminal`'s `applyAddon` method:
|
||||
|
||||
```javascript
|
||||
import { Terminal } from xterm;
|
||||
import { Terminal } from 'xterm';
|
||||
import * as fit from 'xterm/lib/addons/fit/fit';
|
||||
|
||||
|
||||
Terminal.applyAddon(fit);
|
||||
|
||||
var xterm = new Terminal(); // Instantiate the terminal
|
||||
@@ -87,25 +75,16 @@ You will also need to include the addon's CSS file if it has one in the folder.
|
||||
|
||||
#### Importing Addons in TypeScript
|
||||
|
||||
There are currently no typings for addons if they are accessed via extending Terminal prototype, so you will need to upcast if using TypeScript, eg. `(<any>xterm).fit()`.
|
||||
|
||||
Alternatively, you can import addon function and enhance the terminal on demand. This would have better typing support and is friendly to treeshaking. E.g.:
|
||||
There are currently no typings for addons if they are accessed via extending Terminal prototype, so you will need to upcast if using TypeScript, eg. `(xterm as any).fit()`. Alternatively, you can import the addon function and enhance the terminal on demand. This has better typing support and is friendly to treeshaking.
|
||||
|
||||
```typescript
|
||||
import { Terminal } from 'xterm';
|
||||
import { fit } from 'xterm/lib/addons/fit/fit';
|
||||
const xterm = new Terminal();
|
||||
|
||||
// Fit the terminal when necessary:
|
||||
fit(xterm);
|
||||
fit(xterm); // Fit the terminal when necessary
|
||||
```
|
||||
|
||||
#### Third party addons
|
||||
|
||||
There are also the following third party addons available:
|
||||
|
||||
- [xterm-webfont](https://www.npmjs.com/package/xterm-webfont)
|
||||
|
||||
## Browser Support
|
||||
|
||||
Since xterm.js is typically implemented as a developer tool, only modern browsers are supported officially. Here is a list of the versions we aim to support:
|
||||
@@ -116,11 +95,13 @@ Since xterm.js is typically implemented as a developer tool, only modern browser
|
||||
- Safari latest
|
||||
- IE11
|
||||
|
||||
Xterm.js works seamlessly in Electron apps and may even work on earlier versions of the browsers but these are the browsers we strive to keep working.
|
||||
Xterm.js works seamlessly in [Electron](https://electronjs.org/) apps and may even work on earlier versions of the browsers, these are the versions we strive to keep working.
|
||||
|
||||
## API
|
||||
|
||||
The current full API documentation is available in the [TypeScript declaration file on the repository](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts), switch the tag (press `w` when viewing the file) to point at the specific version tag you're using.
|
||||
The full API for xterm.js is contained within the [TypeScript declaration file](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts), use the branch/tag picker in GitHub (`w`) to navigate to the correct version of the API.
|
||||
|
||||
Note that some APIs are marked *experimental*, these are added to enable experimentation with new ideas without committing to support it like a normal [semver](https://semver.org/) API. Note that these APIs can change radically between versions so be sure to read release notes if you plan on using experimental APIs.
|
||||
|
||||
## Real-world uses
|
||||
Xterm.js is used in several world-class applications to provide great terminal experiences.
|
||||
@@ -182,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.
|
||||
|
||||
The existing releases are available at this GitHub repo's [Releases](https://github.com/sourcelair/xterm.js/releases), while the roadmap is available as [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), while a rough roadmap is available by looking through [Milestones](https://github.com/sourcelair/xterm.js/milestones).
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
+17
-19
@@ -14,6 +14,7 @@ import * as fullscreen from '../lib/addons/fullscreen/fullscreen';
|
||||
import * as search from '../lib/addons/search/search';
|
||||
import * as webLinks from '../lib/addons/webLinks/webLinks';
|
||||
import * as winptyCompat from '../lib/addons/winptyCompat/winptyCompat';
|
||||
import { ISearchOptions } from '../lib/addons/search/Interfaces';
|
||||
|
||||
// Pulling in the module's types relies on the <reference> above, it's looks a
|
||||
// little weird here as we're importing "this" module
|
||||
@@ -50,6 +51,14 @@ function setPadding(): void {
|
||||
term.fit();
|
||||
}
|
||||
|
||||
function getSearchOptions(): ISearchOptions {
|
||||
return {
|
||||
regex: (document.getElementById('regex') as HTMLInputElement).checked,
|
||||
wholeWord: (document.getElementById('whole-word') as HTMLInputElement).checked,
|
||||
caseSensitive: (document.getElementById('case-sensitive') as HTMLInputElement).checked
|
||||
};
|
||||
}
|
||||
|
||||
createTerminal();
|
||||
|
||||
const disposeRecreateButtonHandler = () => {
|
||||
@@ -97,26 +106,15 @@ function createTerminal(): void {
|
||||
|
||||
addDomListener(paddingElement, 'change', setPadding);
|
||||
|
||||
addDomListener(actionElements.findNext, 'keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
const searchOptions = {
|
||||
regex: (document.getElementById('regex') as HTMLInputElement).checked,
|
||||
wholeWord: (document.getElementById('whole-word') as HTMLInputElement).checked,
|
||||
caseSensitive: (document.getElementById('case-sensitive') as HTMLInputElement).checked
|
||||
};
|
||||
term.findNext(actionElements.findNext.value, searchOptions);
|
||||
}
|
||||
addDomListener(actionElements.findNext, 'keyup', (e) => {
|
||||
const searchOptions = getSearchOptions();
|
||||
searchOptions.incremental = e.key !== `Enter`;
|
||||
term.findNext(actionElements.findNext.value, searchOptions);
|
||||
});
|
||||
addDomListener(actionElements.findPrevious, 'keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
const searchOptions = {
|
||||
regex: (document.getElementById('regex') as HTMLInputElement).checked,
|
||||
wholeWord: (document.getElementById('whole-word') as HTMLInputElement).checked,
|
||||
caseSensitive: (document.getElementById('case-sensitive') as HTMLInputElement).checked
|
||||
};
|
||||
term.findPrevious(actionElements.findPrevious.value, searchOptions);
|
||||
|
||||
addDomListener(actionElements.findPrevious, 'keyup', (e) => {
|
||||
if (e.key === `Enter`) {
|
||||
term.findPrevious(actionElements.findPrevious.value, getSearchOptions());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+90
-84
@@ -1,100 +1,106 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
var expressWs = require('express-ws')(app);
|
||||
var expressWs = require('express-ws');
|
||||
var os = require('os');
|
||||
var pty = require('node-pty');
|
||||
|
||||
var terminals = {},
|
||||
logs = {};
|
||||
function startServer() {
|
||||
var app = express();
|
||||
expressWs(app);
|
||||
|
||||
app.use('/build', express.static(__dirname + '/../build'));
|
||||
var terminals = {},
|
||||
logs = {};
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.sendFile(__dirname + '/index.html');
|
||||
});
|
||||
app.use('/build', express.static(__dirname + '/../build'));
|
||||
|
||||
app.get('/style.css', function(req, res){
|
||||
res.sendFile(__dirname + '/style.css');
|
||||
});
|
||||
|
||||
app.get('/dist/client-bundle.js', function(req, res){
|
||||
res.sendFile(__dirname + '/dist/client-bundle.js');
|
||||
});
|
||||
|
||||
app.post('/terminals', function (req, res) {
|
||||
var cols = parseInt(req.query.cols),
|
||||
rows = parseInt(req.query.rows),
|
||||
term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], {
|
||||
name: 'xterm-color',
|
||||
cols: cols || 80,
|
||||
rows: rows || 24,
|
||||
cwd: process.env.PWD,
|
||||
env: process.env
|
||||
});
|
||||
|
||||
console.log('Created terminal with PID: ' + term.pid);
|
||||
terminals[term.pid] = term;
|
||||
logs[term.pid] = '';
|
||||
term.on('data', function(data) {
|
||||
logs[term.pid] += data;
|
||||
app.get('/', function(req, res){
|
||||
res.sendFile(__dirname + '/index.html');
|
||||
});
|
||||
res.send(term.pid.toString());
|
||||
res.end();
|
||||
});
|
||||
|
||||
app.post('/terminals/:pid/size', function (req, res) {
|
||||
var pid = parseInt(req.params.pid),
|
||||
cols = parseInt(req.query.cols),
|
||||
rows = parseInt(req.query.rows),
|
||||
term = terminals[pid];
|
||||
app.get('/style.css', function(req, res){
|
||||
res.sendFile(__dirname + '/style.css');
|
||||
});
|
||||
|
||||
term.resize(cols, rows);
|
||||
console.log('Resized terminal ' + pid + ' to ' + cols + ' cols and ' + rows + ' rows.');
|
||||
res.end();
|
||||
});
|
||||
app.get('/dist/client-bundle.js', function(req, res){
|
||||
res.sendFile(__dirname + '/dist/client-bundle.js');
|
||||
});
|
||||
|
||||
app.ws('/terminals/:pid', function (ws, req) {
|
||||
var term = terminals[parseInt(req.params.pid)];
|
||||
console.log('Connected to terminal ' + term.pid);
|
||||
ws.send(logs[term.pid]);
|
||||
app.post('/terminals', function (req, res) {
|
||||
var cols = parseInt(req.query.cols),
|
||||
rows = parseInt(req.query.rows),
|
||||
term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], {
|
||||
name: 'xterm-color',
|
||||
cols: cols || 80,
|
||||
rows: rows || 24,
|
||||
cwd: process.env.PWD,
|
||||
env: process.env
|
||||
});
|
||||
|
||||
function buffer(socket, timeout) {
|
||||
let s = '';
|
||||
let sender = null;
|
||||
return (data) => {
|
||||
s += data;
|
||||
if (!sender) {
|
||||
sender = setTimeout(() => {
|
||||
socket.send(s);
|
||||
s = '';
|
||||
sender = null;
|
||||
}, timeout);
|
||||
}
|
||||
};
|
||||
}
|
||||
const send = buffer(ws, 5);
|
||||
console.log('Created terminal with PID: ' + term.pid);
|
||||
terminals[term.pid] = term;
|
||||
logs[term.pid] = '';
|
||||
term.on('data', function(data) {
|
||||
logs[term.pid] += data;
|
||||
});
|
||||
res.send(term.pid.toString());
|
||||
res.end();
|
||||
});
|
||||
|
||||
term.on('data', function(data) {
|
||||
try {
|
||||
send(data);
|
||||
} catch (ex) {
|
||||
// The WebSocket is not open, ignore
|
||||
app.post('/terminals/:pid/size', function (req, res) {
|
||||
var pid = parseInt(req.params.pid),
|
||||
cols = parseInt(req.query.cols),
|
||||
rows = parseInt(req.query.rows),
|
||||
term = terminals[pid];
|
||||
|
||||
term.resize(cols, rows);
|
||||
console.log('Resized terminal ' + pid + ' to ' + cols + ' cols and ' + rows + ' rows.');
|
||||
res.end();
|
||||
});
|
||||
|
||||
app.ws('/terminals/:pid', function (ws, req) {
|
||||
var term = terminals[parseInt(req.params.pid)];
|
||||
console.log('Connected to terminal ' + term.pid);
|
||||
ws.send(logs[term.pid]);
|
||||
|
||||
function buffer(socket, timeout) {
|
||||
let s = '';
|
||||
let sender = null;
|
||||
return (data) => {
|
||||
s += data;
|
||||
if (!sender) {
|
||||
sender = setTimeout(() => {
|
||||
socket.send(s);
|
||||
s = '';
|
||||
sender = null;
|
||||
}, timeout);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
term.write(msg);
|
||||
});
|
||||
ws.on('close', function () {
|
||||
term.kill();
|
||||
console.log('Closed terminal ' + term.pid);
|
||||
// Clean things up
|
||||
delete terminals[term.pid];
|
||||
delete logs[term.pid];
|
||||
});
|
||||
});
|
||||
const send = buffer(ws, 5);
|
||||
|
||||
var port = process.env.PORT || 3000,
|
||||
host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0';
|
||||
term.on('data', function(data) {
|
||||
try {
|
||||
send(data);
|
||||
} catch (ex) {
|
||||
// The WebSocket is not open, ignore
|
||||
}
|
||||
});
|
||||
ws.on('message', function(msg) {
|
||||
term.write(msg);
|
||||
});
|
||||
ws.on('close', function () {
|
||||
term.kill();
|
||||
console.log('Closed terminal ' + term.pid);
|
||||
// Clean things up
|
||||
delete terminals[term.pid];
|
||||
delete logs[term.pid];
|
||||
});
|
||||
});
|
||||
|
||||
console.log('App listening to http://' + host + ':' + port);
|
||||
app.listen(port, host);
|
||||
var port = process.env.PORT || 3000,
|
||||
host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0';
|
||||
|
||||
console.log('App listening to http://' + host + ':' + port);
|
||||
app.listen(port, host);
|
||||
}
|
||||
|
||||
module.exports = startServer;
|
||||
|
||||
+7
-2
@@ -8,9 +8,9 @@
|
||||
const cp = require('child_process');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const startServer = require('./server.js');
|
||||
|
||||
// Launch server
|
||||
cp.spawn('node', [path.resolve(__dirname, 'server.js')], { stdio: 'inherit' });
|
||||
startServer();
|
||||
|
||||
// Build/watch client source
|
||||
const clientConfig = {
|
||||
@@ -22,6 +22,11 @@ const clientConfig = {
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: ["source-map-loader"],
|
||||
enforce: "pre"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
/// <reference path="../../typings/xterm.d.ts" />
|
||||
|
||||
import { Terminal } from 'xterm';
|
||||
import { Terminal, IDisposable } from 'xterm';
|
||||
|
||||
namespace constructor {
|
||||
{
|
||||
@@ -119,6 +119,12 @@ namespace methods_core {
|
||||
const t: Terminal = new Terminal();
|
||||
t.attachCustomKeyEventHandler((e: KeyboardEvent) => true);
|
||||
t.attachCustomKeyEventHandler((e: KeyboardEvent) => false);
|
||||
const d1: IDisposable = t.addCsiHandler("x",
|
||||
(params: number[], collect: string): boolean => params[0]===1);
|
||||
d1.dispose();
|
||||
const d2: IDisposable = t.addOscHandler(199,
|
||||
(data: string): boolean => true);
|
||||
d2.dispose();
|
||||
}
|
||||
namespace options {
|
||||
{
|
||||
|
||||
+2
-1
@@ -34,7 +34,7 @@
|
||||
"nodemon": "1.10.2",
|
||||
"nyc": "^11.8.0",
|
||||
"sorcery": "^0.10.0",
|
||||
"source-map-loader": "^0.2.3",
|
||||
"source-map-loader": "^0.2.4",
|
||||
"ts-loader": "^4.5.0",
|
||||
"tslint": "^5.9.1",
|
||||
"tslint-consistent-codestyle": "^1.13.0",
|
||||
@@ -47,6 +47,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node demo/start",
|
||||
"start-debug": "node --inspect-brk demo/start",
|
||||
"start-zmodem": "node demo/zmodem/app",
|
||||
"lint": "tslint 'src/**/*.ts' './demo/**/*.ts'",
|
||||
"pretest": "npm run layering",
|
||||
|
||||
+31
-2
@@ -355,7 +355,7 @@ describe('Buffer', () => {
|
||||
let terminal: TestTerminal;
|
||||
|
||||
beforeEach(() => {
|
||||
terminal = new TestTerminal({rows: 5, cols: 10});
|
||||
terminal = new TestTerminal({rows: 5, cols: 10, scrollback: 5});
|
||||
});
|
||||
|
||||
it('multiline ascii', () => {
|
||||
@@ -516,9 +516,38 @@ describe('Buffer', () => {
|
||||
assert.deepEqual([(j / terminal.cols) | 0, j % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('test fully wrapped buffer up to last char', () => {
|
||||
const input = Array(6).join('1234567890');
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0]).get(bufferIndex[1])[CHAR_DATA_CHAR_INDEX]);
|
||||
}
|
||||
});
|
||||
|
||||
it('test fully wrapped buffer up to last char with full width odd', () => {
|
||||
const input = 'a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301'
|
||||
+ 'a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.equal(
|
||||
(!(i % 3))
|
||||
? input[i]
|
||||
: (i % 3 === 1)
|
||||
? input.substr(i, 2)
|
||||
: input.substr(i - 1, 2),
|
||||
terminal.buffer.lines.get(bufferIndex[0]).get(bufferIndex[1])[CHAR_DATA_CHAR_INDEX]);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('BufferStringIterator', function(): void {
|
||||
it('iterator does not ovrflow buffer limits', function(): void {
|
||||
it('iterator does not overflow buffer limits', function(): void {
|
||||
const terminal = new TestTerminal({rows: 5, cols: 10, scrollback: 5});
|
||||
const data = [
|
||||
'aaaaaaaaaa',
|
||||
|
||||
+2
-2
@@ -252,7 +252,7 @@ export class Buffer implements IBuffer {
|
||||
while (stringIndex) {
|
||||
const line = this.lines.get(lineIndex);
|
||||
if (!line) {
|
||||
[-1, -1];
|
||||
return [-1, -1];
|
||||
}
|
||||
for (let i = 0; i < line.length; ++i) {
|
||||
stringIndex -= line.get(i)[CHAR_DATA_CHAR_INDEX].length;
|
||||
@@ -275,7 +275,7 @@ export class Buffer implements IBuffer {
|
||||
* @param startCol The column to start at.
|
||||
* @param endCol The column to end at.
|
||||
*/
|
||||
public translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol: number = 0, endCol: number = null): string {
|
||||
public translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol: number = 0, endCol?: number): string {
|
||||
const line = this.lines.get(lineIndex);
|
||||
if (!line) {
|
||||
return '';
|
||||
|
||||
@@ -325,5 +325,10 @@ describe('BufferLine', function(): void {
|
||||
chai.expect(line.translateToString(false)).equal(' ');
|
||||
chai.expect(line.translateToString(true)).equal('');
|
||||
});
|
||||
it('should work with endCol=0', () => {
|
||||
const line = new TestBufferLine(10, [DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE], false);
|
||||
line.set(0, [1, 'a', 1, 'a'.charCodeAt(0)]);
|
||||
chai.expect(line.translateToString(true, 0, 0)).equal('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+6
-8
@@ -118,13 +118,12 @@ export class BufferLineJSArray implements IBufferLine {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public translateToString(trimRight: boolean = false, startCol: number = 0, endCol: number = null): string {
|
||||
let length = endCol || this.length;
|
||||
public translateToString(trimRight: boolean = false, startCol: number = 0, endCol: number = this.length): string {
|
||||
if (trimRight) {
|
||||
length = Math.min(length, this.getTrimmedLength());
|
||||
endCol = Math.min(endCol, this.getTrimmedLength());
|
||||
}
|
||||
let result = '';
|
||||
while (startCol < length) {
|
||||
while (startCol < endCol) {
|
||||
result += this.get(startCol)[CHAR_DATA_CHAR_INDEX] || WHITESPACE_CELL_CHAR;
|
||||
startCol += this.get(startCol)[CHAR_DATA_WIDTH_INDEX] || 1;
|
||||
}
|
||||
@@ -305,13 +304,12 @@ export class BufferLine implements IBufferLine {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public translateToString(trimRight: boolean = false, startCol: number = 0, endCol: number = null): string {
|
||||
let length = endCol || this.length;
|
||||
public translateToString(trimRight: boolean = false, startCol: number = 0, endCol: number = this.length): string {
|
||||
if (trimRight) {
|
||||
length = Math.min(length, this.getTrimmedLength());
|
||||
endCol = Math.min(endCol, this.getTrimmedLength());
|
||||
}
|
||||
let result = '';
|
||||
while (startCol < length) {
|
||||
while (startCol < endCol) {
|
||||
const stringData = this._data[startCol * CELL_SIZE + Cell.STRING];
|
||||
result += (stringData & IS_COMBINED_BIT_MASK) ? this._combined[startCol] : (stringData) ? String.fromCharCode(stringData) : WHITESPACE_CELL_CHAR;
|
||||
startCol += this._data[startCol * CELL_SIZE + Cell.WIDTH] || 1;
|
||||
|
||||
@@ -1187,6 +1187,73 @@ describe('EscapeSequenceParser', function (): void {
|
||||
parse(parser2, INPUT);
|
||||
chai.expect(csi).eql([]);
|
||||
});
|
||||
describe('CSI custom handlers', () => {
|
||||
it('Prevent fallback', () => {
|
||||
const csiCustom: [string, number[], string][] = [];
|
||||
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
||||
parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; });
|
||||
parser2.parse(INPUT);
|
||||
chai.expect(csi).eql([], 'Should not fallback to original handler');
|
||||
chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||
});
|
||||
it('Allow fallback', () => {
|
||||
const csiCustom: [string, number[], string][] = [];
|
||||
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
||||
parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return false; });
|
||||
parser2.parse(INPUT);
|
||||
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']], 'Should fallback to original handler');
|
||||
chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||
});
|
||||
it('Multiple custom handlers fallback once', () => {
|
||||
const csiCustom: [string, number[], string][] = [];
|
||||
const csiCustom2: [string, number[], string][] = [];
|
||||
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) => { csiCustom2.push(['m', params, collect]); return false; });
|
||||
parser2.parse(INPUT);
|
||||
chai.expect(csi).eql([], 'Should not fallback to original handler');
|
||||
chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||
chai.expect(csiCustom2).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||
});
|
||||
it('Multiple custom handlers no fallback', () => {
|
||||
const csiCustom: [string, number[], string][] = [];
|
||||
const csiCustom2: [string, number[], string][] = [];
|
||||
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) => { csiCustom2.push(['m', params, collect]); return true; });
|
||||
parser2.parse(INPUT);
|
||||
chai.expect(csi).eql([], 'Should not fallback to original handler');
|
||||
chai.expect(csiCustom).eql([], 'Should not fallback once');
|
||||
chai.expect(csiCustom2).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||
});
|
||||
it('Execution order should go from latest handler down to the original', () => {
|
||||
const order: number[] = [];
|
||||
parser2.setCsiHandler('m', () => order.push(1));
|
||||
parser2.addCsiHandler('m', () => { order.push(2); return false; });
|
||||
parser2.addCsiHandler('m', () => { order.push(3); return false; });
|
||||
parser2.parse('\x1b[0m');
|
||||
chai.expect(order).eql([3, 2, 1]);
|
||||
});
|
||||
it('Dispose should work', () => {
|
||||
const csiCustom: [string, number[], string][] = [];
|
||||
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
||||
const customHandler = parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; });
|
||||
customHandler.dispose();
|
||||
parser2.parse(INPUT);
|
||||
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||
chai.expect(csiCustom).eql([], 'Should not use custom handler as it was disposed');
|
||||
});
|
||||
it('Should not corrupt the parser when dispose is called twice', () => {
|
||||
const csiCustom: [string, number[], string][] = [];
|
||||
parser2.setCsiHandler('m', (params, collect) => csi.push(['m', params, collect]));
|
||||
const customHandler = parser2.addCsiHandler('m', (params, collect) => { csiCustom.push(['m', params, collect]); return true; });
|
||||
customHandler.dispose();
|
||||
customHandler.dispose();
|
||||
parser2.parse(INPUT);
|
||||
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]);
|
||||
chai.expect(csiCustom).eql([], 'Should not use custom handler as it was disposed');
|
||||
});
|
||||
});
|
||||
it('EXECUTE handler', function (): void {
|
||||
parser2.setExecuteHandler('\n', function (): void {
|
||||
exe.push('\n');
|
||||
@@ -1214,6 +1281,73 @@ describe('EscapeSequenceParser', function (): void {
|
||||
parse(parser2, INPUT);
|
||||
chai.expect(osc).eql([]);
|
||||
});
|
||||
describe('OSC custom handlers', () => {
|
||||
it('Prevent fallback', () => {
|
||||
const oscCustom: [number, string][] = [];
|
||||
parser2.setOscHandler(1, data => osc.push([1, data]));
|
||||
parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
||||
parser2.parse(INPUT);
|
||||
chai.expect(osc).eql([], 'Should not fallback to original handler');
|
||||
chai.expect(oscCustom).eql([[1, 'foo=bar']]);
|
||||
});
|
||||
it('Allow fallback', () => {
|
||||
const oscCustom: [number, string][] = [];
|
||||
parser2.setOscHandler(1, data => osc.push([1, data]));
|
||||
parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return false; });
|
||||
parser2.parse(INPUT);
|
||||
chai.expect(osc).eql([[1, 'foo=bar']], 'Should fallback to original handler');
|
||||
chai.expect(oscCustom).eql([[1, 'foo=bar']]);
|
||||
});
|
||||
it('Multiple custom handlers fallback once', () => {
|
||||
const oscCustom: [number, string][] = [];
|
||||
const oscCustom2: [number, string][] = [];
|
||||
parser2.setOscHandler(1, data => osc.push([1, data]));
|
||||
parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
||||
parser2.addOscHandler(1, data => { oscCustom2.push([1, data]); return false; });
|
||||
parser2.parse(INPUT);
|
||||
chai.expect(osc).eql([], 'Should not fallback to original handler');
|
||||
chai.expect(oscCustom).eql([[1, 'foo=bar']]);
|
||||
chai.expect(oscCustom2).eql([[1, 'foo=bar']]);
|
||||
});
|
||||
it('Multiple custom handlers no fallback', () => {
|
||||
const oscCustom: [number, string][] = [];
|
||||
const oscCustom2: [number, string][] = [];
|
||||
parser2.setOscHandler(1, data => osc.push([1, data]));
|
||||
parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
||||
parser2.addOscHandler(1, data => { oscCustom2.push([1, data]); return true; });
|
||||
parser2.parse(INPUT);
|
||||
chai.expect(osc).eql([], 'Should not fallback to original handler');
|
||||
chai.expect(oscCustom).eql([], 'Should not fallback once');
|
||||
chai.expect(oscCustom2).eql([[1, 'foo=bar']]);
|
||||
});
|
||||
it('Execution order should go from latest handler down to the original', () => {
|
||||
const order: number[] = [];
|
||||
parser2.setOscHandler(1, () => order.push(1));
|
||||
parser2.addOscHandler(1, () => { order.push(2); return false; });
|
||||
parser2.addOscHandler(1, () => { order.push(3); return false; });
|
||||
parser2.parse('\x1b]1;foo=bar\x1b\\');
|
||||
chai.expect(order).eql([3, 2, 1]);
|
||||
});
|
||||
it('Dispose should work', () => {
|
||||
const oscCustom: [number, string][] = [];
|
||||
parser2.setOscHandler(1, data => osc.push([1, data]));
|
||||
const customHandler = parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
||||
customHandler.dispose();
|
||||
parser2.parse(INPUT);
|
||||
chai.expect(osc).eql([[1, 'foo=bar']]);
|
||||
chai.expect(oscCustom).eql([], 'Should not use custom handler as it was disposed');
|
||||
});
|
||||
it('Should not corrupt the parser when dispose is called twice', () => {
|
||||
const oscCustom: [number, string][] = [];
|
||||
parser2.setOscHandler(1, data => osc.push([1, data]));
|
||||
const customHandler = parser2.addOscHandler(1, data => { oscCustom.push([1, data]); return true; });
|
||||
customHandler.dispose();
|
||||
customHandler.dispose();
|
||||
parser2.parse(INPUT);
|
||||
chai.expect(osc).eql([[1, 'foo=bar']]);
|
||||
chai.expect(oscCustom).eql([], 'Should not use custom handler as it was disposed');
|
||||
});
|
||||
});
|
||||
it('DCS handler', function (): void {
|
||||
parser2.setDcsHandler('+p', {
|
||||
hook: function (collect: string, params: number[], flag: number): void {
|
||||
|
||||
+66
-11
@@ -4,9 +4,17 @@
|
||||
*/
|
||||
|
||||
import { ParserState, ParserAction, IParsingState, IDcsHandler, IEscapeSequenceParser } from './Types';
|
||||
import { IDisposable } from 'xterm';
|
||||
import { Disposable } from './common/Lifecycle';
|
||||
import { utf16ToString } from './common/TypedArrayUtils';
|
||||
|
||||
interface IHandlerCollection<T> {
|
||||
[key: string]: T[];
|
||||
}
|
||||
|
||||
type CsiHandler = (params: number[], collect: string) => boolean | void;
|
||||
type OscHandler = (data: string) => boolean | void;
|
||||
|
||||
/**
|
||||
* Returns an array filled with numbers between the low and high parameters (right exclusive).
|
||||
* @param low The low number.
|
||||
@@ -224,9 +232,9 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
// handler lookup containers
|
||||
protected _printHandler: (data: Uint16Array, start: number, end: number) => void;
|
||||
protected _executeHandlers: any;
|
||||
protected _csiHandlers: any;
|
||||
protected _csiHandlers: IHandlerCollection<CsiHandler>;
|
||||
protected _escHandlers: any;
|
||||
protected _oscHandlers: any;
|
||||
protected _oscHandlers: IHandlerCollection<OscHandler>;
|
||||
protected _dcsHandlers: any;
|
||||
protected _activeDcsHandler: IDcsHandler | null;
|
||||
protected _errorHandler: (state: IParsingState) => IParsingState;
|
||||
@@ -280,8 +288,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._errorHandlerFb = null;
|
||||
this._printHandler = null;
|
||||
this._executeHandlers = null;
|
||||
this._csiHandlers = null;
|
||||
this._escHandlers = null;
|
||||
this._csiHandlers = null;
|
||||
this._oscHandlers = null;
|
||||
this._dcsHandlers = null;
|
||||
this._activeDcsHandler = null;
|
||||
@@ -305,8 +313,24 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._executeHandlerFb = callback;
|
||||
}
|
||||
|
||||
addCsiHandler(flag: string, callback: CsiHandler): IDisposable {
|
||||
const index = flag.charCodeAt(0);
|
||||
if (this._csiHandlers[index] === undefined) {
|
||||
this._csiHandlers[index] = [];
|
||||
}
|
||||
const handlerList = this._csiHandlers[index];
|
||||
handlerList.push(callback);
|
||||
return {
|
||||
dispose: () => {
|
||||
const handlerIndex = handlerList.indexOf(callback);
|
||||
if (handlerIndex !== -1) {
|
||||
handlerList.splice(handlerIndex, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
setCsiHandler(flag: string, callback: (params: number[], collect: string) => void): void {
|
||||
this._csiHandlers[flag.charCodeAt(0)] = callback;
|
||||
this._csiHandlers[flag.charCodeAt(0)] = [callback];
|
||||
}
|
||||
clearCsiHandler(flag: string): void {
|
||||
if (this._csiHandlers[flag.charCodeAt(0)]) delete this._csiHandlers[flag.charCodeAt(0)];
|
||||
@@ -325,8 +349,23 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._escHandlerFb = callback;
|
||||
}
|
||||
|
||||
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
|
||||
if (this._oscHandlers[ident] === undefined) {
|
||||
this._oscHandlers[ident] = [];
|
||||
}
|
||||
const handlerList = this._oscHandlers[ident];
|
||||
handlerList.push(callback);
|
||||
return {
|
||||
dispose: () => {
|
||||
const handlerIndex = handlerList.indexOf(callback);
|
||||
if (handlerIndex !== -1) {
|
||||
handlerList.splice(handlerIndex, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
setOscHandler(ident: number, callback: (data: string) => void): void {
|
||||
this._oscHandlers[ident] = callback;
|
||||
this._oscHandlers[ident] = [callback];
|
||||
}
|
||||
clearOscHandler(ident: number): void {
|
||||
if (this._oscHandlers[ident]) delete this._oscHandlers[ident];
|
||||
@@ -462,9 +501,17 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
}
|
||||
break;
|
||||
case ParserAction.CSI_DISPATCH:
|
||||
callback = this._csiHandlers[code];
|
||||
if (callback) callback(params, collect);
|
||||
else this._csiHandlerFb(collect, params, code);
|
||||
// Trigger CSI Handler
|
||||
const handlers = this._csiHandlers[code];
|
||||
let j = handlers ? handlers.length - 1 : -1;
|
||||
for (; j >= 0; j--) {
|
||||
if (handlers[j](params, collect)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < 0) {
|
||||
this._csiHandlerFb(collect, params, code);
|
||||
}
|
||||
break;
|
||||
case ParserAction.PARAM:
|
||||
if (code === 0x3b) params.push(0);
|
||||
@@ -539,9 +586,17 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
// or with an explicit NaN OSC handler
|
||||
const identifier = parseInt(osc.substring(0, idx));
|
||||
const content = osc.substring(idx + 1);
|
||||
callback = this._oscHandlers[identifier];
|
||||
if (callback) callback(content);
|
||||
else this._oscHandlerFb(identifier, content);
|
||||
// Trigger OSC Handler
|
||||
const handlers = this._oscHandlers[identifier];
|
||||
let j = handlers ? handlers.length - 1 : -1;
|
||||
for (; j >= 0; j--) {
|
||||
if (handlers[j](content)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < 0) {
|
||||
this._oscHandlerFb(identifier, content);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (code === 0x1b) transition |= ParserState.ESCAPE;
|
||||
|
||||
@@ -12,6 +12,7 @@ import { FLAGS } from './renderer/Types';
|
||||
import { wcwidth } from './CharWidth';
|
||||
import { EscapeSequenceParser } from './EscapeSequenceParser';
|
||||
import { ICharset } from './core/Types';
|
||||
import { IDisposable } from 'xterm';
|
||||
import { Disposable } from './common/Lifecycle';
|
||||
import { concat, utf16ToString } from './common/TypedArrayUtils';
|
||||
|
||||
@@ -481,6 +482,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._terminal.updateRange(buffer.y);
|
||||
}
|
||||
|
||||
addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable {
|
||||
return this._parser.addCsiHandler(flag, callback);
|
||||
}
|
||||
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
|
||||
return this._parser.addOscHandler(ident, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* BEL
|
||||
* Bell (Ctrl-G).
|
||||
|
||||
@@ -219,9 +219,17 @@ export class Linkifier extends EventEmitter implements ILinkifier {
|
||||
// also correct regex and string search offsets for the next loop run
|
||||
stringIndex = text.indexOf(uri, stringIndex + 1);
|
||||
rex.lastIndex = stringIndex + uri.length;
|
||||
if (stringIndex < 0) {
|
||||
// invalid stringIndex (should not have happened)
|
||||
break;
|
||||
}
|
||||
|
||||
// get the buffer index as [absolute row, col] for the match
|
||||
const bufferIndex = this._terminal.buffer.stringIndexToBufferIndex(rowIndex, stringIndex);
|
||||
if (bufferIndex[0] < 0) {
|
||||
// invalid bufferIndex (should not have happened)
|
||||
break;
|
||||
}
|
||||
|
||||
const line = this._terminal.buffer.lines.get(bufferIndex[0]);
|
||||
const char = line.get(bufferIndex[1]);
|
||||
|
||||
@@ -198,7 +198,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
}
|
||||
} else {
|
||||
// Get first row
|
||||
const startRowEndCol = start[1] === end[1] ? end[0] : null;
|
||||
const startRowEndCol = start[1] === end[1] ? end[0] : undefined;
|
||||
result.push(this._buffer.translateBufferLineToString(start[1], true, start[0], startRowEndCol));
|
||||
|
||||
// Get middle rows
|
||||
|
||||
@@ -1413,6 +1413,15 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
this._customKeyEventHandler = customKeyEventHandler;
|
||||
}
|
||||
|
||||
/** Add handler for CSI escape sequence. See xterm.d.ts for details. */
|
||||
public addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable {
|
||||
return this._inputHandler.addCsiHandler(flag, callback);
|
||||
}
|
||||
/** Add handler for OSC escape sequence. See xterm.d.ts for details. */
|
||||
public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
|
||||
return this._inputHandler.addOscHandler(ident, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a link matcher, allowing custom link patterns to be matched and
|
||||
* handled.
|
||||
|
||||
@@ -492,6 +492,8 @@ export interface IEscapeSequenceParser extends IDisposable {
|
||||
setCsiHandler(flag: string, callback: (params: number[], collect: string) => void): void;
|
||||
clearCsiHandler(flag: string): void;
|
||||
setCsiHandlerFallback(callback: (collect: string, params: number[], flag: number) => void): void;
|
||||
addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable;
|
||||
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable;
|
||||
|
||||
setEscHandler(collectAndFlag: string, callback: () => void): void;
|
||||
clearEscHandler(collectAndFlag: string): void;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user