Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f30c38f773 | ||
|
|
bfe11b7081 | ||
|
|
a800dd3aab | ||
|
|
a5a0779aa9 | ||
|
|
4fe5a5a0bb | ||
|
|
974c721f66 | ||
|
|
851fb7ae38 | ||
|
|
1c72a66567 | ||
|
|
6f8919ab93 | ||
|
|
dac2da05bf | ||
|
|
7487d61fe2 | ||
|
|
67e5974eee | ||
|
|
21c3b3243d | ||
|
|
4bd721c749 | ||
|
|
ee2c1f71ef | ||
|
|
a721e4884d | ||
|
|
31be1ebc53 | ||
|
|
be59c74eae | ||
|
|
c1a0b26150 | ||
|
|
13ddaf8f98 | ||
|
|
6dc6748876 | ||
|
|
527379d343 | ||
|
|
5ea016d537 | ||
|
|
bb424bd86f | ||
|
|
a6f4855f66 | ||
|
|
fe08f34918 | ||
|
|
68d6d5576c | ||
|
|
a4d561d3b9 | ||
|
|
c223524f77 | ||
|
|
f8fa962ec7 | ||
|
|
2a46f5f147 | ||
|
|
080188c468 | ||
|
|
1392abb6eb | ||
|
|
30d3d46027 | ||
|
|
6d620166b4 | ||
|
|
276463e95e | ||
|
|
84e4cf7e9c | ||
|
|
d9f58c0dbb | ||
|
|
3e753f1370 | ||
|
|
616d341e8c | ||
|
|
15392c6c87 | ||
|
|
5e2001f4b5 | ||
|
|
5194098f12 | ||
|
|
3ce552cdec | ||
|
|
720c8c4623 | ||
|
|
243874c644 | ||
|
|
5d547bffb0 | ||
|
|
495f97cf95 | ||
|
|
5c2479944e | ||
|
|
51ef0d0680 | ||
|
|
2b39887a37 | ||
|
|
7c82f9c7d4 | ||
|
|
1dbe482fcb | ||
|
|
cfd158cbff | ||
|
|
9826f3a14c | ||
|
|
6f7c91d0cd | ||
|
|
bcdebe7648 | ||
|
|
09b244ed2f | ||
|
|
7576e8c202 | ||
|
|
7b752be68a | ||
|
|
d02086e5c6 | ||
|
|
5c172c74b2 | ||
|
|
d29fc6d5de | ||
|
|
449ede1d57 | ||
|
|
fc10816615 | ||
|
|
631107698e | ||
|
|
33d58ec3d2 | ||
|
|
f445bc763b | ||
|
|
f8a62a23e5 | ||
|
|
a92635b22c | ||
|
|
44a4bd788f | ||
|
|
eb252433ce | ||
|
|
499d6c26f0 | ||
|
|
8350804858 | ||
|
|
9df547d746 | ||
|
|
7537adfb72 | ||
|
|
caf075dcb0 | ||
|
|
57581ca4ef | ||
|
|
a604389cc0 | ||
|
|
08475eb495 | ||
|
|
91d1f97754 | ||
|
|
990be060f2 | ||
|
|
29e53c9c11 | ||
|
|
da42738881 | ||
|
|
9753a7d425 | ||
|
|
07308485a4 | ||
|
|
3d500ba802 | ||
|
|
e8dc563ccf | ||
|
|
ee245230c9 | ||
|
|
843ea1b610 | ||
|
|
9f80ce25c2 | ||
|
|
bd57e305e1 | ||
|
|
740d261d7a | ||
|
|
1bc246323c | ||
|
|
91cbeecf52 | ||
|
|
cfe8022867 | ||
|
|
99db619a5c | ||
|
|
4f8cda9f3a | ||
|
|
9fdb674f20 | ||
|
|
e76b8f86b5 | ||
|
|
0083c23b2d | ||
|
|
040f9f49d9 | ||
|
|
cbb79a77f9 | ||
|
|
a97fe2df1c | ||
|
|
ff13f74858 | ||
|
|
3d6815ca6f | ||
|
|
da11e31fe5 | ||
|
|
e224b865fd | ||
|
|
821a7afc01 | ||
|
|
84b56b7691 | ||
|
|
8288a26b49 | ||
|
|
d87bae374b | ||
|
|
7c982fc0ce | ||
|
|
1eb6e37187 | ||
|
|
efbdbd3f3b | ||
|
|
7fc57da85a | ||
|
|
6f5e5215f2 | ||
|
|
cad9477eef | ||
|
|
4fde8db060 | ||
|
|
724bcc3766 | ||
|
|
52ae8fc14b | ||
|
|
e14b1f75fe | ||
|
|
da5bf975b4 | ||
|
|
22398c0927 | ||
|
|
43b291112e | ||
|
|
87a1fca726 | ||
|
|
52e211915e | ||
|
|
92c991132f | ||
|
|
89ac2834f1 | ||
|
|
358c09a108 | ||
|
|
ff790236c1 | ||
|
|
9014a27d7d | ||
|
|
5236d4aabf | ||
|
|
ea922fe548 | ||
|
|
22b06cada7 | ||
|
|
5a65286c54 | ||
|
|
0e8883ac99 | ||
|
|
5cb0bcea0f | ||
|
|
2fd24849b2 | ||
|
|
b81711e3da | ||
|
|
0c740213e3 | ||
|
|
f3b7866a19 | ||
|
|
d34d1f56cb | ||
|
|
322565807b | ||
|
|
1178ef2a12 | ||
|
|
6aab756ccd | ||
|
|
d1bd603382 | ||
|
|
191932cce2 | ||
|
|
bc7ba7bd85 | ||
|
|
74ba282056 | ||
|
|
96f944522e | ||
|
|
bf209fb71f | ||
|
|
3b537c7e9f | ||
|
|
ce39c0633c | ||
|
|
aba4d76ec6 | ||
|
|
91734f7659 | ||
|
|
1ac3cf8fca | ||
|
|
c2ec16a16c | ||
|
|
e402772cf7 | ||
|
|
d9ba19ba9e | ||
|
|
a95e451227 | ||
|
|
fe74b5588d | ||
|
|
dcbc7e1014 | ||
|
|
50e552262a | ||
|
|
597757d0ee | ||
|
|
5d3313a8ca | ||
|
|
9d291d1d12 | ||
|
|
7da361af9f | ||
|
|
761e437c69 | ||
|
|
fc4f345446 | ||
|
|
0a4883b2b3 | ||
|
|
293f54e8dd | ||
|
|
05fad38240 | ||
|
|
75ba717bd7 | ||
|
|
19f0fc0aa9 | ||
|
|
ffd27dfef0 | ||
|
|
e042f25b8d | ||
|
|
7f9f757042 | ||
|
|
5e1de3b062 | ||
|
|
54739d625f | ||
|
|
97bf796c43 | ||
|
|
54ff71f982 | ||
|
|
f7827c4df8 | ||
|
|
4b5709c68d | ||
|
|
4fe78dfe34 | ||
|
|
a353a8cffe | ||
|
|
73a17cfacb | ||
|
|
8bc0099434 | ||
|
|
9a903ca1c5 | ||
|
|
5483a052cc | ||
|
|
bd132d1f74 |
@@ -8,12 +8,19 @@ RUN apt-get update \
|
|||||||
# Verify git and process tools are installed
|
# Verify git and process tools are installed
|
||||||
RUN apt-get install -y git procps
|
RUN apt-get install -y git procps
|
||||||
|
|
||||||
# Install yarn
|
# Install yarn, puppeteer deps
|
||||||
RUN apt-get install -y curl apt-transport-https lsb-release \
|
RUN apt-get install -y curl apt-transport-https lsb-release \
|
||||||
&& curl -sS https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/pubkey.gpg | apt-key add - 2>/dev/null \
|
&& curl -sS https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/pubkey.gpg | apt-key add - 2>/dev/null \
|
||||||
&& echo "deb https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
|
&& echo "deb https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get -y install --no-install-recommends yarn
|
&& apt-get -y install --no-install-recommends \
|
||||||
|
yarn fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst ttf-freefont \
|
||||||
|
# https://github.com/Googlechrome/puppeteer/issues/290#issuecomment-322921352
|
||||||
|
gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \
|
||||||
|
libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \
|
||||||
|
libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \
|
||||||
|
libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
|
||||||
|
ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
RUN apt-get autoremove -y \
|
RUN apt-get autoremove -y \
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ Xterm.js is used in several world-class applications to provide great terminal e
|
|||||||
- [**PHP App Server**](https://github.com/cubiclesoft/php-app-server/): Create lightweight, installable almost-native applications for desktop OSes. ExecTerminal (nicely wraps the xterm.js Terminal), TerminalManager, and RunProcessSDK are self-contained, reusable ES5+ compliant Javascript components.
|
- [**PHP App Server**](https://github.com/cubiclesoft/php-app-server/): Create lightweight, installable almost-native applications for desktop OSes. ExecTerminal (nicely wraps the xterm.js Terminal), TerminalManager, and RunProcessSDK are self-contained, reusable ES5+ compliant Javascript components.
|
||||||
- [**NgTerminal**](https://github.com/qwefgh90/ng-terminal): NgTerminal is a web terminal that leverages xterm.js on Angular 7+. You can easily add it into your application by adding `<ng-terminal></ng-terminal>` into your component.
|
- [**NgTerminal**](https://github.com/qwefgh90/ng-terminal): NgTerminal is a web terminal that leverages xterm.js on Angular 7+. You can easily add it into your application by adding `<ng-terminal></ng-terminal>` into your component.
|
||||||
- [**tty-share**](https://tty-share.com): Extremely simple terminal sharing over the Internet.
|
- [**tty-share**](https://tty-share.com): Extremely simple terminal sharing over the Internet.
|
||||||
|
- [**Ten Hands**](https://github.com/saisandeepvaddi/ten-hands): One place to run your command-line tasks.
|
||||||
|
- [**WebAssembly.sh**](https://webassembly.sh): A WebAssembly WASI browser terminal
|
||||||
|
|
||||||
[And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
|
[And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ npm install --save xterm-addon-attach
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { Terminal } from 'xterm';
|
import { Terminal } from 'xterm';
|
||||||
import { FitAddon } from 'xterm-addon-attach';
|
import { AttachAddon } from 'xterm-addon-attach';
|
||||||
|
|
||||||
const terminal = new Terminal();
|
const terminal = new Terminal();
|
||||||
const attachAddon = new AttachAddon(webSocket);
|
const attachAddon = new AttachAddon(webSocket);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm-addon-attach",
|
"name": "xterm-addon-attach",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "The xterm.js authors",
|
"name": "The xterm.js authors",
|
||||||
"url": "https://xtermjs.org/"
|
"url": "https://xtermjs.org/"
|
||||||
@@ -16,6 +16,6 @@
|
|||||||
"prepublishOnly": "npm run package"
|
"prepublishOnly": "npm run package"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"xterm": "^3.14.0"
|
"xterm": "^4.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ describe('AttachAddon', () => {
|
|||||||
browser = await puppeteer.launch({
|
browser = await puppeteer.launch({
|
||||||
headless: process.argv.indexOf('--headless') !== -1,
|
headless: process.argv.indexOf('--headless') !== -1,
|
||||||
slowMo: 80,
|
slowMo: 80,
|
||||||
args: [`--window-size=${width},${height}`]
|
args: [`--window-size=${width},${height}`, `--no-sandbox`]
|
||||||
});
|
});
|
||||||
page = (await browser.pages())[0];
|
page = (await browser.pages())[0];
|
||||||
await page.setViewport({ width, height });
|
await page.setViewport({ width, height });
|
||||||
@@ -54,7 +54,7 @@ describe('AttachAddon', () => {
|
|||||||
const server = new WebSocket.Server({ port });
|
const server = new WebSocket.Server({ port });
|
||||||
const data = new Uint8Array([102, 111, 111]);
|
const data = new Uint8Array([102, 111, 111]);
|
||||||
server.on('connection', socket => socket.send(data));
|
server.on('connection', socket => socket.send(data));
|
||||||
await page.evaluate(`window.term.loadAddon(new window.AttachAddon(new WebSocket('ws://localhost:${port}'), { inputUtf8: true }))`);
|
await page.evaluate(`window.term.loadAddon(new window.AttachAddon(new WebSocket('ws://localhost:${port}')))`);
|
||||||
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo');
|
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo');
|
||||||
server.close();
|
server.close();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,13 +9,11 @@ import { Terminal, IDisposable, ITerminalAddon } from 'xterm';
|
|||||||
|
|
||||||
interface IAttachOptions {
|
interface IAttachOptions {
|
||||||
bidirectional?: boolean;
|
bidirectional?: boolean;
|
||||||
inputUtf8?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AttachAddon implements ITerminalAddon {
|
export class AttachAddon implements ITerminalAddon {
|
||||||
private _socket: WebSocket;
|
private _socket: WebSocket;
|
||||||
private _bidirectional: boolean;
|
private _bidirectional: boolean;
|
||||||
private _utf8: boolean;
|
|
||||||
private _disposables: IDisposable[] = [];
|
private _disposables: IDisposable[] = [];
|
||||||
|
|
||||||
constructor(socket: WebSocket, options?: IAttachOptions) {
|
constructor(socket: WebSocket, options?: IAttachOptions) {
|
||||||
@@ -23,17 +21,15 @@ export class AttachAddon implements ITerminalAddon {
|
|||||||
// always set binary type to arraybuffer, we do not handle blobs
|
// always set binary type to arraybuffer, we do not handle blobs
|
||||||
this._socket.binaryType = 'arraybuffer';
|
this._socket.binaryType = 'arraybuffer';
|
||||||
this._bidirectional = (options && options.bidirectional === false) ? false : true;
|
this._bidirectional = (options && options.bidirectional === false) ? false : true;
|
||||||
this._utf8 = !!(options && options.inputUtf8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public activate(terminal: Terminal): void {
|
public activate(terminal: Terminal): void {
|
||||||
if (this._utf8) {
|
this._disposables.push(
|
||||||
this._disposables.push(addSocketListener(this._socket, 'message',
|
addSocketListener(this._socket, 'message', ev => {
|
||||||
(ev: MessageEvent | Event | CloseEvent) => terminal.writeUtf8(new Uint8Array((ev as any).data as ArrayBuffer))));
|
const data: ArrayBuffer | string = ev.data;
|
||||||
} else {
|
terminal.write(typeof data === 'string' ? data : new Uint8Array(data));
|
||||||
this._disposables.push(addSocketListener(this._socket, 'message',
|
})
|
||||||
(ev: MessageEvent | Event | CloseEvent) => terminal.write((ev as any).data as string)));
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (this._bidirectional) {
|
if (this._bidirectional) {
|
||||||
this._disposables.push(terminal.onData(data => this._sendData(data)));
|
this._disposables.push(terminal.onData(data => this._sendData(data)));
|
||||||
@@ -57,7 +53,7 @@ export class AttachAddon implements ITerminalAddon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addSocketListener(socket: WebSocket, type: string, handler: (this: WebSocket, ev: MessageEvent | Event | CloseEvent) => any): IDisposable {
|
function addSocketListener<K extends keyof WebSocketEventMap>(socket: WebSocket, type: K, handler: (this: WebSocket, ev: WebSocketEventMap[K]) => any): IDisposable {
|
||||||
socket.addEventListener(type, handler);
|
socket.addEventListener(type, handler);
|
||||||
return {
|
return {
|
||||||
dispose: () => {
|
dispose: () => {
|
||||||
|
|||||||
@@ -11,14 +11,6 @@ declare module 'xterm-addon-attach' {
|
|||||||
* Whether input should be written to the backend. Defaults to `true`.
|
* Whether input should be written to the backend. Defaults to `true`.
|
||||||
*/
|
*/
|
||||||
bidirectional?: boolean;
|
bidirectional?: boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to use UTF8 binary transport for incoming messages. Defaults to `false`.
|
|
||||||
* Note: This must be in line with the server side of the websocket.
|
|
||||||
* Always send string messages from the backend if this options is false,
|
|
||||||
* otherwise always binary UTF8 data.
|
|
||||||
*/
|
|
||||||
inputUtf8?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AttachAddon implements ITerminalAddon {
|
export class AttachAddon implements ITerminalAddon {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm-addon-fit",
|
"name": "xterm-addon-fit",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "The xterm.js authors",
|
"name": "The xterm.js authors",
|
||||||
"url": "https://xtermjs.org/"
|
"url": "https://xtermjs.org/"
|
||||||
@@ -16,6 +16,6 @@
|
|||||||
"prepublishOnly": "npm run package"
|
"prepublishOnly": "npm run package"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"xterm": "^3.14.0"
|
"xterm": "^4.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as puppeteer from 'puppeteer';
|
||||||
|
import { assert } from 'chai';
|
||||||
|
import { ITerminalOptions } from 'xterm';
|
||||||
|
|
||||||
|
const APP = 'http://127.0.0.1:3000/test';
|
||||||
|
|
||||||
|
let browser: puppeteer.Browser;
|
||||||
|
let page: puppeteer.Page;
|
||||||
|
const width = 1024;
|
||||||
|
const height = 768;
|
||||||
|
|
||||||
|
describe('FitAddon', () => {
|
||||||
|
before(async function(): Promise<any> {
|
||||||
|
this.timeout(20000);
|
||||||
|
browser = await puppeteer.launch({
|
||||||
|
headless: process.argv.indexOf('--headless') !== -1,
|
||||||
|
slowMo: 80,
|
||||||
|
args: [`--window-size=${width},${height}`, `--no-sandbox`]
|
||||||
|
});
|
||||||
|
page = (await browser.pages())[0];
|
||||||
|
await page.setViewport({ width, height });
|
||||||
|
await page.goto(APP);
|
||||||
|
await openTerminal();
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('no terminal', async function(): Promise<any> {
|
||||||
|
await page.evaluate(`window.fit = new FitAddon();`);
|
||||||
|
assert.equal(await page.evaluate(`window.fit.proposeDimensions()`), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('proposeDimensions', () => {
|
||||||
|
afterEach(async () => {
|
||||||
|
return unloadFit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('default', async function(): Promise<any> {
|
||||||
|
await loadFit();
|
||||||
|
assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), {
|
||||||
|
cols: 87,
|
||||||
|
rows: 26
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('width', async function(): Promise<any> {
|
||||||
|
await loadFit(1008);
|
||||||
|
assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), {
|
||||||
|
cols: 110,
|
||||||
|
rows: 26
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('small', async function(): Promise<any> {
|
||||||
|
await loadFit(1, 1);
|
||||||
|
assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), {
|
||||||
|
cols: 2,
|
||||||
|
rows: 1
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fit', () => {
|
||||||
|
afterEach(async () => {
|
||||||
|
return unloadFit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('default', async function(): Promise<any> {
|
||||||
|
await loadFit();
|
||||||
|
await page.evaluate(`window.fit.fit()`);
|
||||||
|
assert.equal(await page.evaluate(`window.term.cols`), 87);
|
||||||
|
assert.equal(await page.evaluate(`window.term.rows`), 26);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('width', async function(): Promise<any> {
|
||||||
|
await loadFit(1008);
|
||||||
|
await page.evaluate(`window.fit.fit()`);
|
||||||
|
assert.equal(await page.evaluate(`window.term.cols`), 110);
|
||||||
|
assert.equal(await page.evaluate(`window.term.rows`), 26);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('small', async function(): Promise<any> {
|
||||||
|
await loadFit(1, 1);
|
||||||
|
await page.evaluate(`window.fit.fit()`);
|
||||||
|
assert.equal(await page.evaluate(`window.term.cols`), 2);
|
||||||
|
assert.equal(await page.evaluate(`window.term.rows`), 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadFit(width: number = 800, height: number = 450): Promise<void> {
|
||||||
|
await page.evaluate(`
|
||||||
|
window.fit = new FitAddon();
|
||||||
|
window.term.loadAddon(window.fit);
|
||||||
|
document.querySelector('#terminal-container').style.width='${width}px';
|
||||||
|
document.querySelector('#terminal-container').style.height='${height}px';
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function unloadFit(): Promise<void> {
|
||||||
|
await page.evaluate(`window.fit.dispose();`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
|
||||||
|
await page.evaluate(`window.term = new Terminal(${JSON.stringify(options)})`);
|
||||||
|
await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);
|
||||||
|
if (options.rendererType === 'dom') {
|
||||||
|
await page.waitForSelector('.xterm-rows');
|
||||||
|
} else {
|
||||||
|
await page.waitForSelector('.xterm-text-layer');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,9 @@ interface ITerminalDimensions {
|
|||||||
cols: number;
|
cols: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MINIMUM_COLS = 2;
|
||||||
|
const MINIMUM_ROWS = 1;
|
||||||
|
|
||||||
export class FitAddon implements ITerminalAddon {
|
export class FitAddon implements ITerminalAddon {
|
||||||
private _terminal: Terminal | undefined;
|
private _terminal: Terminal | undefined;
|
||||||
|
|
||||||
@@ -49,7 +52,7 @@ export class FitAddon implements ITerminalAddon {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._terminal.element.parentElement) {
|
if (!this._terminal.element || !this._terminal.element.parentElement) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,8 +74,8 @@ export class FitAddon implements ITerminalAddon {
|
|||||||
const availableHeight = parentElementHeight - elementPaddingVer;
|
const availableHeight = parentElementHeight - elementPaddingVer;
|
||||||
const availableWidth = parentElementWidth - elementPaddingHor - core.viewport.scrollBarWidth;
|
const availableWidth = parentElementWidth - elementPaddingHor - core.viewport.scrollBarWidth;
|
||||||
const geometry = {
|
const geometry = {
|
||||||
cols: Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth),
|
cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth)),
|
||||||
rows: Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight)
|
rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight))
|
||||||
};
|
};
|
||||||
return geometry;
|
return geometry;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm-addon-search",
|
"name": "xterm-addon-search",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "The xterm.js authors",
|
"name": "The xterm.js authors",
|
||||||
"url": "https://xtermjs.org/"
|
"url": "https://xtermjs.org/"
|
||||||
@@ -16,6 +16,6 @@
|
|||||||
"prepublishOnly": "npm run package"
|
"prepublishOnly": "npm run package"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"xterm": "^3.14.0"
|
"xterm": "^4.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ const width = 800;
|
|||||||
const height = 600;
|
const height = 600;
|
||||||
|
|
||||||
describe('Search Tests', function (): void {
|
describe('Search Tests', function (): void {
|
||||||
this.timeout(200000);
|
this.timeout(20000);
|
||||||
|
|
||||||
before(async function (): Promise<any> {
|
before(async function (): Promise<any> {
|
||||||
browser = await puppeteer.launch({
|
browser = await puppeteer.launch({
|
||||||
headless: process.argv.indexOf('--headless') !== -1,
|
headless: process.argv.indexOf('--headless') !== -1,
|
||||||
slowMo: 80,
|
slowMo: 80,
|
||||||
args: [`--window-size=${width},${height}`]
|
args: [`--window-size=${width},${height}`, `--no-sandbox`]
|
||||||
});
|
});
|
||||||
page = (await browser.pages())[0];
|
page = (await browser.pages())[0];
|
||||||
await page.setViewport({ width, height });
|
await page.setViewport({ width, height });
|
||||||
@@ -98,6 +98,14 @@ describe('Search Tests', function (): void {
|
|||||||
await page.evaluate(`window.search.findNext('[A-Z]+', {regex: true, caseSensitive: true})`);
|
await page.evaluate(`window.search.findNext('[A-Z]+', {regex: true, caseSensitive: true})`);
|
||||||
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'ABCD');
|
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'ABCD');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Search for single result twice should not unselect it', async () => {
|
||||||
|
await writeSync('abc def');
|
||||||
|
assert.deepEqual(await page.evaluate(`window.search.findNext('abc')`), true);
|
||||||
|
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc');
|
||||||
|
assert.deepEqual(await page.evaluate(`window.search.findNext('abc')`), true);
|
||||||
|
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
|
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
|
||||||
@@ -111,12 +119,7 @@ async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function writeSync(data: string): Promise<void> {
|
async function writeSync(data: string): Promise<void> {
|
||||||
await page.evaluate(`window.term.write('${data}');`);
|
return page.evaluate(`new Promise(resolve => window.term.write('${data}', resolve))`);
|
||||||
while (true) {
|
|
||||||
if (await page.evaluate(`window.term._core.writeBuffer.length === 0`)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeData(length: number): string {
|
function makeData(length: number): string {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Terminal, IDisposable, ITerminalAddon } from 'xterm';
|
import { Terminal, IDisposable, ITerminalAddon, ISelectionPosition } from 'xterm';
|
||||||
|
|
||||||
export interface ISearchOptions {
|
export interface ISearchOptions {
|
||||||
regex?: boolean;
|
regex?: boolean;
|
||||||
@@ -59,12 +59,12 @@ export class SearchAddon implements ITerminalAddon {
|
|||||||
|
|
||||||
let startCol = 0;
|
let startCol = 0;
|
||||||
let startRow = 0;
|
let startRow = 0;
|
||||||
|
let currentSelection: ISelectionPosition | undefined;
|
||||||
if (this._terminal.hasSelection()) {
|
if (this._terminal.hasSelection()) {
|
||||||
const incremental = searchOptions ? searchOptions.incremental : false;
|
const incremental = searchOptions ? searchOptions.incremental : false;
|
||||||
// Start from the selection end if there is a selection
|
// Start from the selection end if there is a selection
|
||||||
// For incremental search, use existing row
|
// For incremental search, use existing row
|
||||||
const currentSelection = this._terminal.getSelectionPosition()!;
|
currentSelection = this._terminal.getSelectionPosition()!;
|
||||||
startRow = incremental ? currentSelection.startRow : currentSelection.endRow;
|
startRow = incremental ? currentSelection.startRow : currentSelection.endRow;
|
||||||
startCol = incremental ? currentSelection.startColumn : currentSelection.endColumn;
|
startCol = incremental ? currentSelection.startColumn : currentSelection.endColumn;
|
||||||
}
|
}
|
||||||
@@ -97,6 +97,9 @@ export class SearchAddon implements ITerminalAddon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is only one result, return true.
|
||||||
|
if (!result && currentSelection) return true;
|
||||||
|
|
||||||
// Set selection and scroll if a result was found
|
// Set selection and scroll if a result was found
|
||||||
return this._selectResult(result);
|
return this._selectResult(result);
|
||||||
}
|
}
|
||||||
@@ -121,10 +124,11 @@ export class SearchAddon implements ITerminalAddon {
|
|||||||
const isReverseSearch = true;
|
const isReverseSearch = true;
|
||||||
let startRow = this._terminal.buffer.baseY + this._terminal.rows;
|
let startRow = this._terminal.buffer.baseY + this._terminal.rows;
|
||||||
let startCol = this._terminal.cols;
|
let startCol = this._terminal.cols;
|
||||||
let result: ISearchResult | undefined = undefined;
|
let result: ISearchResult | undefined;
|
||||||
const incremental = searchOptions ? searchOptions.incremental : false;
|
const incremental = searchOptions ? searchOptions.incremental : false;
|
||||||
|
let currentSelection: ISelectionPosition | undefined;
|
||||||
if (this._terminal.hasSelection()) {
|
if (this._terminal.hasSelection()) {
|
||||||
const currentSelection = this._terminal.getSelectionPosition()!;
|
currentSelection = this._terminal.getSelectionPosition()!;
|
||||||
// Start from selection start if there is a selection
|
// Start from selection start if there is a selection
|
||||||
startRow = currentSelection.startRow;
|
startRow = currentSelection.startRow;
|
||||||
startCol = currentSelection.startColumn;
|
startCol = currentSelection.startColumn;
|
||||||
@@ -161,6 +165,9 @@ export class SearchAddon implements ITerminalAddon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is only one result, return true.
|
||||||
|
if (!result && currentSelection) return true;
|
||||||
|
|
||||||
// Set selection and scroll if a result was found
|
// Set selection and scroll if a result was found
|
||||||
return this._selectResult(result);
|
return this._selectResult(result);
|
||||||
}
|
}
|
||||||
@@ -344,7 +351,7 @@ export class SearchAddon implements ITerminalAddon {
|
|||||||
}
|
}
|
||||||
terminal.select(result.col, result.row, result.term.length);
|
terminal.select(result.col, result.row, result.term.length);
|
||||||
// If it is not in the viewport then we scroll else it just gets selected
|
// If it is not in the viewport then we scroll else it just gets selected
|
||||||
if (result.row > (terminal.buffer.viewportY + terminal.rows) || result.row < terminal.buffer.viewportY) {
|
if (result.row >= (terminal.buffer.viewportY + terminal.rows) || result.row < terminal.buffer.viewportY) {
|
||||||
let scroll = result.row - terminal.buffer.viewportY;
|
let scroll = result.row - terminal.buffer.viewportY;
|
||||||
scroll = scroll - Math.floor(terminal.rows / 2);
|
scroll = scroll - Math.floor(terminal.rows / 2);
|
||||||
terminal.scrollLines(scroll);
|
terminal.scrollLines(scroll);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm-addon-web-links",
|
"name": "xterm-addon-web-links",
|
||||||
"version": "0.2.0",
|
"version": "0.2.1",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "The xterm.js authors",
|
"name": "The xterm.js authors",
|
||||||
"url": "https://xtermjs.org/"
|
"url": "https://xtermjs.org/"
|
||||||
@@ -16,6 +16,6 @@
|
|||||||
"prepublishOnly": "npm run package"
|
"prepublishOnly": "npm run package"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"xterm": "^3.14.0"
|
"xterm": "^4.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ describe('WebLinksAddon', () => {
|
|||||||
browser = await puppeteer.launch({
|
browser = await puppeteer.launch({
|
||||||
headless: process.argv.indexOf('--headless') !== -1,
|
headless: process.argv.indexOf('--headless') !== -1,
|
||||||
slowMo: 80,
|
slowMo: 80,
|
||||||
args: [`--window-size=${width},${height}`]
|
args: [`--window-size=${width},${height}`, `--no-sandbox`]
|
||||||
});
|
});
|
||||||
page = (await browser.pages())[0];
|
page = (await browser.pages())[0];
|
||||||
await page.setViewport({ width, height });
|
await page.setViewport({ width, height });
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm-addon-webgl",
|
"name": "xterm-addon-webgl",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "The xterm.js authors",
|
"name": "The xterm.js authors",
|
||||||
"url": "https://xtermjs.org/"
|
"url": "https://xtermjs.org/"
|
||||||
@@ -16,6 +16,6 @@
|
|||||||
"prepublishOnly": "npm run package"
|
"prepublishOnly": "npm run package"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"xterm": "^3.14.0"
|
"xterm": "^4.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,9 +105,6 @@ export class GlyphRenderer {
|
|||||||
const gl = this._gl;
|
const gl = this._gl;
|
||||||
|
|
||||||
const program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource));
|
const program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource));
|
||||||
if (program === undefined) {
|
|
||||||
throw new Error('Could not create WebGL program');
|
|
||||||
}
|
|
||||||
this._program = program;
|
this._program = program;
|
||||||
|
|
||||||
// Uniform locations
|
// Uniform locations
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ describe('WebGL Renderer Integration Tests', function(): void {
|
|||||||
browser = await puppeteer.launch({
|
browser = await puppeteer.launch({
|
||||||
headless: process.argv.indexOf('--headless') !== -1,
|
headless: process.argv.indexOf('--headless') !== -1,
|
||||||
slowMo: 80,
|
slowMo: 80,
|
||||||
args: [`--window-size=${width},${height}`]
|
args: [`--window-size=${width},${height}`, `--no-sandbox`]
|
||||||
});
|
});
|
||||||
page = (await browser.pages())[0];
|
page = (await browser.pages())[0];
|
||||||
await page.setViewport({ width, height });
|
await page.setViewport({ width, height });
|
||||||
@@ -145,12 +145,7 @@ async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function writeSync(data: string): Promise<void> {
|
async function writeSync(data: string): Promise<void> {
|
||||||
await page.evaluate(`window.term.write('${data}');`);
|
return page.evaluate(`new Promise(resolve => window.term.write('${data}', resolve))`);
|
||||||
while (true) {
|
|
||||||
if (await page.evaluate(`window.term._core.writeBuffer.length === 0`)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getCellColor(col: number, row: number): Promise<number[]> {
|
async function getCellColor(col: number, row: number): Promise<number[]> {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { IWebGL2RenderingContext } from './Types';
|
|||||||
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
||||||
import { RenderModel, COMBINED_CHAR_BIT_MASK } from './RenderModel';
|
import { RenderModel, COMBINED_CHAR_BIT_MASK } from './RenderModel';
|
||||||
import { Disposable } from 'common/Lifecycle';
|
import { Disposable } from 'common/Lifecycle';
|
||||||
import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CODE } from 'common/buffer/Constants';
|
import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX, NULL_CELL_CODE } from 'common/buffer/Constants';
|
||||||
import { Terminal } from 'xterm';
|
import { Terminal } from 'xterm';
|
||||||
import { getLuminance } from './ColorUtils';
|
import { getLuminance } from './ColorUtils';
|
||||||
import { IRenderLayer } from './renderLayer/Types';
|
import { IRenderLayer } from './renderLayer/Types';
|
||||||
|
|||||||
@@ -92,10 +92,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
|||||||
if (this._cursorBlinkStateManager) {
|
if (this._cursorBlinkStateManager) {
|
||||||
this._cursorBlinkStateManager.dispose();
|
this._cursorBlinkStateManager.dispose();
|
||||||
}
|
}
|
||||||
// Request a refresh from the terminal as management of rendering is being
|
|
||||||
// moved back to the terminal
|
|
||||||
terminal.refresh(terminal.buffer.cursorY, terminal.buffer.cursorY);
|
|
||||||
}
|
}
|
||||||
|
// Request a refresh from the terminal as management of rendering is being
|
||||||
|
// moved back to the terminal
|
||||||
|
terminal.refresh(terminal.buffer.cursorY, terminal.buffer.cursorY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onCursorMove(terminal: Terminal): void {
|
public onCursorMove(terminal: Terminal): void {
|
||||||
@@ -139,12 +139,17 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
|||||||
this._clearCursor();
|
this._clearCursor();
|
||||||
this._ctx.save();
|
this._ctx.save();
|
||||||
this._ctx.fillStyle = this._colors.cursor.css;
|
this._ctx.fillStyle = this._colors.cursor.css;
|
||||||
this._renderBlurCursor(terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell);
|
const cursorStyle = terminal.getOption('cursorStyle');
|
||||||
|
if (cursorStyle && cursorStyle !== 'block') {
|
||||||
|
this._cursorRenderers[cursorStyle](terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell);
|
||||||
|
} else {
|
||||||
|
this._renderBlurCursor(terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell);
|
||||||
|
}
|
||||||
this._ctx.restore();
|
this._ctx.restore();
|
||||||
this._state.x = terminal.buffer.cursorX;
|
this._state.x = terminal.buffer.cursorX;
|
||||||
this._state.y = viewportRelativeCursorY;
|
this._state.y = viewportRelativeCursorY;
|
||||||
this._state.isFocused = false;
|
this._state.isFocused = false;
|
||||||
this._state.style = terminal.getOption('cursorStyle');
|
this._state.style = cursorStyle;
|
||||||
this._state.width = this._cell.getWidth();
|
this._state.width = this._cell.getWidth();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-8
@@ -20,7 +20,7 @@ jobs:
|
|||||||
yarn
|
yarn
|
||||||
displayName: 'Install dependencies and build'
|
displayName: 'Install dependencies and build'
|
||||||
- script: |
|
- script: |
|
||||||
yarn test-unit
|
yarn test-unit --forbid-only
|
||||||
displayName: 'Unit tests'
|
displayName: 'Unit tests'
|
||||||
- script: |
|
- script: |
|
||||||
yarn lint
|
yarn lint
|
||||||
@@ -38,7 +38,7 @@ jobs:
|
|||||||
yarn
|
yarn
|
||||||
displayName: 'Install dependencies and build'
|
displayName: 'Install dependencies and build'
|
||||||
- script: |
|
- script: |
|
||||||
yarn test-unit
|
yarn test-unit --forbid-only
|
||||||
displayName: 'Unit tests'
|
displayName: 'Unit tests'
|
||||||
- script: |
|
- script: |
|
||||||
yarn lint
|
yarn lint
|
||||||
@@ -56,7 +56,7 @@ jobs:
|
|||||||
yarn
|
yarn
|
||||||
displayName: 'Install dependencies and build'
|
displayName: 'Install dependencies and build'
|
||||||
- script: |
|
- script: |
|
||||||
yarn test-unit
|
yarn test-unit --forbid-only
|
||||||
displayName: 'Unit tests'
|
displayName: 'Unit tests'
|
||||||
- script: |
|
- script: |
|
||||||
yarn lint
|
yarn lint
|
||||||
@@ -80,7 +80,7 @@ jobs:
|
|||||||
- script: |
|
- script: |
|
||||||
yarn start &
|
yarn start &
|
||||||
sleep 10
|
sleep 10
|
||||||
yarn test-api --headless
|
yarn test-api --headless --forbid-only
|
||||||
displayName: 'Linux Integration tests'
|
displayName: 'Linux Integration tests'
|
||||||
|
|
||||||
- job: macOS_IntegrationTests
|
- job: macOS_IntegrationTests
|
||||||
@@ -97,7 +97,7 @@ jobs:
|
|||||||
- script: |
|
- script: |
|
||||||
yarn start &
|
yarn start &
|
||||||
sleep 10
|
sleep 10
|
||||||
yarn test-api --headless
|
yarn test-api --headless --forbid-only
|
||||||
displayName: 'MacOS Integration tests'
|
displayName: 'MacOS Integration tests'
|
||||||
|
|
||||||
- job: Release
|
- job: Release
|
||||||
@@ -107,7 +107,7 @@ jobs:
|
|||||||
- Windows
|
- Windows
|
||||||
- Linux_IntegrationTests
|
- Linux_IntegrationTests
|
||||||
- macOS_IntegrationTests
|
- macOS_IntegrationTests
|
||||||
condition: and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['Build.SourceBranch'], 'refs/heads/release/*')))
|
condition: and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['FORCE_RELEASE'], 'true')))
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'ubuntu-16.04'
|
vmImage: 'ubuntu-16.04'
|
||||||
steps:
|
steps:
|
||||||
@@ -121,8 +121,7 @@ jobs:
|
|||||||
displayName: 'Install Yarn'
|
displayName: 'Install Yarn'
|
||||||
- script: |
|
- script: |
|
||||||
yarn
|
yarn
|
||||||
BUILD_DIR=dist npm run build
|
|
||||||
displayName: 'Install dependencies and build'
|
displayName: 'Install dependencies and build'
|
||||||
- script: |
|
- script: |
|
||||||
NPM_AUTH_TOKEN="$(NPM_AUTH_TOKEN)" node ./bin/publish.js
|
NPM_AUTH_TOKEN="$(NPM_AUTH_TOKEN)" node ./bin/publish.js
|
||||||
displayName: 'Publish to npm'
|
displayName: 'Package and publish to npm'
|
||||||
|
|||||||
+19
-1
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
const cp = require('child_process');
|
const cp = require('child_process');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const os = require('os');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
// Setup auth
|
// Setup auth
|
||||||
@@ -18,8 +19,9 @@ if (isDryRun) {
|
|||||||
const changedFiles = getChangedFilesInCommit('HEAD');
|
const changedFiles = getChangedFilesInCommit('HEAD');
|
||||||
|
|
||||||
// Publish xterm if any files were changed outside of the addons directory
|
// Publish xterm if any files were changed outside of the addons directory
|
||||||
|
let isStableRelease = false;
|
||||||
if (changedFiles.some(e => e.search(/^addons\//) === -1)) {
|
if (changedFiles.some(e => e.search(/^addons\//) === -1)) {
|
||||||
checkAndPublishPackage(path.resolve(__dirname, '..'));
|
isStableRelease = checkAndPublishPackage(path.resolve(__dirname, '..'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publish addons if any files were changed inside of the addon
|
// Publish addons if any files were changed inside of the addon
|
||||||
@@ -39,6 +41,11 @@ addonPackageDirs.forEach(p => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Publish website if it's a stable release
|
||||||
|
if (isStableRelease) {
|
||||||
|
updateWebsite();
|
||||||
|
}
|
||||||
|
|
||||||
function checkAndPublishPackage(packageDir) {
|
function checkAndPublishPackage(packageDir) {
|
||||||
const packageJson = require(path.join(packageDir, 'package.json'));
|
const packageJson = require(path.join(packageDir, 'package.json'));
|
||||||
|
|
||||||
@@ -76,6 +83,8 @@ function checkAndPublishPackage(packageDir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
|
|
||||||
|
return isStableRelease;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNextBetaVersion(packageJson) {
|
function getNextBetaVersion(packageJson) {
|
||||||
@@ -115,3 +124,12 @@ function getChangedFilesInCommit(commit) {
|
|||||||
const changedFiles = output.split('\n').filter(e => e.length > 0);
|
const changedFiles = output.split('\n').filter(e => e.length > 0);
|
||||||
return changedFiles;
|
return changedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateWebsite() {
|
||||||
|
console.log('Updating website');
|
||||||
|
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'website-'));
|
||||||
|
const packageJson = require(path.join(path.resolve(__dirname, '..'), 'package.json'));
|
||||||
|
if (!isDryRun) {
|
||||||
|
cp.spawnSync('sh', [path.join(__dirname, 'update-website.sh'), packageJson.version], { cwd, stdio: [process.stdin, process.stdout, process.stderr] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+12
-5
@@ -15,15 +15,22 @@ let testFiles = [
|
|||||||
'./out/**/*test.js'
|
'./out/**/*test.js'
|
||||||
];
|
];
|
||||||
|
|
||||||
// ability to inject particular test files via
|
let flagArgs = [];
|
||||||
// yarn test [testFileA testFileB ...]
|
|
||||||
if (process.argv.length > 2) {
|
if (process.argv.length > 2) {
|
||||||
testFiles = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
|
flagArgs = args.filter(e => e.startsWith('--'));
|
||||||
|
// ability to inject particular test files via
|
||||||
|
// yarn test [testFileA testFileB ...]
|
||||||
|
files = args.filter(e => !e.startsWith('--'));
|
||||||
|
if (files.length) {
|
||||||
|
testFiles = files;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const run = cp.spawnSync(
|
const run = cp.spawnSync(
|
||||||
path.resolve(__dirname, '../node_modules/.bin/mocha'),
|
path.resolve(__dirname, '../node_modules/.bin/mocha'),
|
||||||
testFiles,
|
[...testFiles, ...flagArgs],
|
||||||
{
|
{
|
||||||
cwd: path.resolve(__dirname, '..'),
|
cwd: path.resolve(__dirname, '..'),
|
||||||
env,
|
env,
|
||||||
@@ -31,4 +38,4 @@ const run = cp.spawnSync(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
process.exit(run.status);
|
process.exit(run.status);
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Name the arguments
|
||||||
|
VERSION=$1
|
||||||
|
|
||||||
|
# Clone docs repo and update the documentation
|
||||||
|
git clone https://github.com/xtermjs/xtermjs.org
|
||||||
|
cd xtermjs.org
|
||||||
|
yarn
|
||||||
|
./bin/update-docs
|
||||||
|
|
||||||
|
# Add changes to index and only proceed if there are changes to commit
|
||||||
|
touch test-file
|
||||||
|
git add .
|
||||||
|
if ! git diff-index --quiet HEAD --; then
|
||||||
|
|
||||||
|
# Delete the upstream branch if it exists for some reason
|
||||||
|
export BRANCH_NAME=update-$VERSION
|
||||||
|
git branch -D $BRANCH_NAME || true
|
||||||
|
git push origin :$BRANCH_NAME || true
|
||||||
|
|
||||||
|
# Create commit and push it to update-x.y.z
|
||||||
|
git checkout -b $BRANCH_NAME
|
||||||
|
git config --global user.name Daniel Imms
|
||||||
|
git config --global user.email tyriar@tyriar.com
|
||||||
|
git commit -m 'Update docs for v$VERSION'
|
||||||
|
git push --set-upstream origin update-4.2.0
|
||||||
|
git push -f
|
||||||
|
|
||||||
|
# Create a PR in the GitHub repo
|
||||||
|
curl \
|
||||||
|
-H "Authorization: token $GITHUB_TOKEN" \
|
||||||
|
-X POST \
|
||||||
|
-d "{\"title\":\"Update docs for v$VERSION\",\"base\":\"master\",\"head\":\"xtermjs:$BRANCH_NAME\"}" \
|
||||||
|
https://api.github.com/repos/xtermjs/xtermjs.org/pulls
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
echo "No changes to commit"
|
||||||
|
|
||||||
|
fi
|
||||||
+3
-9
@@ -167,14 +167,7 @@ function createTerminal(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function runRealTerminal(): void {
|
function runRealTerminal(): void {
|
||||||
/**
|
|
||||||
* The demo defaults to string transport by default.
|
|
||||||
* To run it with UTF8 binary transport, swap comment on
|
|
||||||
* the lines below. (Must also be switched in server.js)
|
|
||||||
*/
|
|
||||||
term.loadAddon(new AttachAddon(socket));
|
term.loadAddon(new AttachAddon(socket));
|
||||||
// term.loadAddon(new AttachAddon(socket, {inputUtf8: true}));
|
|
||||||
|
|
||||||
term._initialized = true;
|
term._initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,6 +221,7 @@ function initOptions(term: TerminalType): void {
|
|||||||
bellSound: null,
|
bellSound: null,
|
||||||
bellStyle: ['none', 'sound'],
|
bellStyle: ['none', 'sound'],
|
||||||
cursorStyle: ['block', 'underline', 'bar'],
|
cursorStyle: ['block', 'underline', 'bar'],
|
||||||
|
fastScrollModifier: ['alt', 'ctrl', 'shift', undefined],
|
||||||
fontFamily: null,
|
fontFamily: null,
|
||||||
fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
|
fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
|
||||||
fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
|
fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
|
||||||
@@ -260,7 +254,7 @@ function initOptions(term: TerminalType): void {
|
|||||||
});
|
});
|
||||||
html += '</div><div class="option-group">';
|
html += '</div><div class="option-group">';
|
||||||
numberOptions.forEach(o => {
|
numberOptions.forEach(o => {
|
||||||
html += `<div class="option"><label>${o} <input id="opt-${o}" type="number" value="${term.getOption(o)}" step="${o === 'lineHeight' ? '0.1' : '1'}"/></label></div>`;
|
html += `<div class="option"><label>${o} <input id="opt-${o}" type="number" value="${term.getOption(o)}" step="${o === 'lineHeight' || o === 'scrollSensitivity' ? '0.1' : '1'}"/></label></div>`;
|
||||||
});
|
});
|
||||||
html += '</div><div class="option-group">';
|
html += '</div><div class="option-group">';
|
||||||
Object.keys(stringOptions).forEach(o => {
|
Object.keys(stringOptions).forEach(o => {
|
||||||
@@ -289,7 +283,7 @@ function initOptions(term: TerminalType): void {
|
|||||||
console.log('change', o, input.value);
|
console.log('change', o, input.value);
|
||||||
if (o === 'cols' || o === 'rows') {
|
if (o === 'cols' || o === 'rows') {
|
||||||
updateTerminalSize();
|
updateTerminalSize();
|
||||||
} else if (o === 'lineHeight') {
|
} else if (o === 'lineHeight' || o === 'scrollSensitivity') {
|
||||||
term.setOption(o, parseFloat(input.value));
|
term.setOption(o, parseFloat(input.value));
|
||||||
updateTerminalSize();
|
updateTerminalSize();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+12
-14
@@ -3,12 +3,8 @@ var expressWs = require('express-ws');
|
|||||||
var os = require('os');
|
var os = require('os');
|
||||||
var pty = require('node-pty');
|
var pty = require('node-pty');
|
||||||
|
|
||||||
/**
|
// Whether to use binary transport.
|
||||||
* Whether to use UTF8 binary transport.
|
const USE_BINARY = os.platform() !== "win32";
|
||||||
* (Must also be switched in client.ts)
|
|
||||||
*/
|
|
||||||
const USE_BINARY_UTF8 = false;
|
|
||||||
|
|
||||||
|
|
||||||
function startServer() {
|
function startServer() {
|
||||||
var app = express();
|
var app = express();
|
||||||
@@ -18,24 +14,26 @@ function startServer() {
|
|||||||
logs = {};
|
logs = {};
|
||||||
|
|
||||||
app.use('/xterm.css', express.static(__dirname + '/../css/xterm.css'));
|
app.use('/xterm.css', express.static(__dirname + '/../css/xterm.css'));
|
||||||
app.get('/logo.png', (req, res) => res.sendFile(__dirname + '/logo.png'));
|
app.get('/logo.png', (req, res) => { // lgtm [js/missing-rate-limiting]
|
||||||
|
res.sendFile(__dirname + '/logo.png');
|
||||||
|
});
|
||||||
|
|
||||||
app.get('/', function(req, res){
|
app.get('/', (req, res) => { // lgtm [js/missing-rate-limiting]
|
||||||
res.sendFile(__dirname + '/index.html');
|
res.sendFile(__dirname + '/index.html');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/test', function(req, res){
|
app.get('/test', (req, res) => { // lgtm [js/missing-rate-limiting]
|
||||||
res.sendFile(__dirname + '/test.html');
|
res.sendFile(__dirname + '/test.html');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/style.css', function(req, res){
|
app.get('/style.css', (req, res) => { // lgtm [js/missing-rate-limiting]
|
||||||
res.sendFile(__dirname + '/style.css');
|
res.sendFile(__dirname + '/style.css');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use('/dist', express.static(__dirname + '/dist'));
|
app.use('/dist', express.static(__dirname + '/dist'));
|
||||||
app.use('/src', express.static(__dirname + '/src'));
|
app.use('/src', express.static(__dirname + '/src'));
|
||||||
|
|
||||||
app.post('/terminals', function (req, res) {
|
app.post('/terminals', (req, res) => {
|
||||||
const env = Object.assign({}, process.env);
|
const env = Object.assign({}, process.env);
|
||||||
env['COLORTERM'] = 'truecolor';
|
env['COLORTERM'] = 'truecolor';
|
||||||
var cols = parseInt(req.query.cols),
|
var cols = parseInt(req.query.cols),
|
||||||
@@ -46,7 +44,7 @@ function startServer() {
|
|||||||
rows: rows || 24,
|
rows: rows || 24,
|
||||||
cwd: env.PWD,
|
cwd: env.PWD,
|
||||||
env: env,
|
env: env,
|
||||||
encoding: USE_BINARY_UTF8 ? null : 'utf8'
|
encoding: USE_BINARY ? null : 'utf8'
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Created terminal with PID: ' + term.pid);
|
console.log('Created terminal with PID: ' + term.pid);
|
||||||
@@ -59,7 +57,7 @@ function startServer() {
|
|||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/terminals/:pid/size', function (req, res) {
|
app.post('/terminals/:pid/size', (req, res) => {
|
||||||
var pid = parseInt(req.params.pid),
|
var pid = parseInt(req.params.pid),
|
||||||
cols = parseInt(req.query.cols),
|
cols = parseInt(req.query.cols),
|
||||||
rows = parseInt(req.query.rows),
|
rows = parseInt(req.query.rows),
|
||||||
@@ -108,7 +106,7 @@ function startServer() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const send = USE_BINARY_UTF8 ? bufferUtf8(ws, 5) : buffer(ws, 5);
|
const send = USE_BINARY ? bufferUtf8(ws, 5) : buffer(ws, 5);
|
||||||
|
|
||||||
term.on('data', function(data) {
|
term.on('data', function(data) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB[10;15r[5;5H[5 A[12;12H[5 A[20;20H[5 A
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB[10;15r[5;5H[5 @[12;12H[5 @[20;20H[5 @
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB[10;15r[5;5H[5'}[12;12H[5'}[20;20H[5'}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
|
||||||
|
abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
|
||||||
|
abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
|
||||||
|
abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
|
||||||
|
abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
|
||||||
|
abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB[10;15r[5;5H[5'~[12;12H[5'~[20;20H[5'~
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
|
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm",
|
"name": "xterm",
|
||||||
"description": "Full xterm terminal, in your browser",
|
"description": "Full xterm terminal, in your browser",
|
||||||
"version": "4.0.0",
|
"version": "4.2.0",
|
||||||
"main": "lib/xterm.js",
|
"main": "lib/xterm.js",
|
||||||
"style": "css/xterm.css",
|
"style": "css/xterm.css",
|
||||||
"types": "typings/xterm.d.ts",
|
"types": "typings/xterm.d.ts",
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
"ts-loader": "^6.0.4",
|
"ts-loader": "^6.0.4",
|
||||||
"tslint": "^5.18.0",
|
"tslint": "^5.18.0",
|
||||||
"tslint-consistent-codestyle": "^1.13.0",
|
"tslint-consistent-codestyle": "^1.13.0",
|
||||||
"typescript": "3.5",
|
"typescript": "3.6",
|
||||||
"utf8": "^3.0.0",
|
"utf8": "^3.0.0",
|
||||||
"webpack": "^4.35.3",
|
"webpack": "^4.35.3",
|
||||||
"webpack-cli": "^3.1.0",
|
"webpack-cli": "^3.1.0",
|
||||||
|
|||||||
@@ -25,6 +25,14 @@ function getCursor(term: TestTerminal): number[] {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLines(term: TestTerminal, limit: number = term.rows): string[] {
|
||||||
|
const res: string[] = [];
|
||||||
|
for (let i = 0; i < limit; ++i) {
|
||||||
|
res.push(term.buffer.lines.get(i).translateToString(true));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
describe('InputHandler', () => {
|
describe('InputHandler', () => {
|
||||||
describe('save and restore cursor', () => {
|
describe('save and restore cursor', () => {
|
||||||
const terminal = new MockInputHandlingTerminal();
|
const terminal = new MockInputHandlingTerminal();
|
||||||
@@ -1125,13 +1133,6 @@ describe('InputHandler', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
term = new TestTerminal({cols: 10, rows: 10});
|
term = new TestTerminal({cols: 10, rows: 10});
|
||||||
});
|
});
|
||||||
function getLines(term: TestTerminal, limit: number = term.rows): string[] {
|
|
||||||
const res: string[] = [];
|
|
||||||
for (let i = 0; i < limit; ++i) {
|
|
||||||
res.push(term.buffer.lines.get(i).translateToString(true));
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
it('scrollUp', () => {
|
it('scrollUp', () => {
|
||||||
term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Sm');
|
term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Sm');
|
||||||
assert.deepEqual(getLines(term), ['m', '3', '', '', '4', '5', '6', '7', '8', '9']);
|
assert.deepEqual(getLines(term), ['m', '3', '', '', '4', '5', '6', '7', '8', '9']);
|
||||||
@@ -1191,4 +1192,78 @@ describe('InputHandler', () => {
|
|||||||
assert.deepEqual(getLines(term), ['0', '1', 'n', 'm', '', '', '6', '7', '8', '9']);
|
assert.deepEqual(getLines(term), ['0', '1', 'n', 'm', '', '', '6', '7', '8', '9']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('SL/SR/DECIC/DECDC', () => {
|
||||||
|
let term: TestTerminal;
|
||||||
|
beforeEach(() => {
|
||||||
|
term = new TestTerminal({cols: 5, rows: 5, scrollback: 1});
|
||||||
|
});
|
||||||
|
it('SL (scrollLeft)', () => {
|
||||||
|
term.writeSync('12345'.repeat(6));
|
||||||
|
term.writeSync('\x1b[ @');
|
||||||
|
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '2345', '2345', '2345', '2345', '2345']);
|
||||||
|
term.writeSync('\x1b[0 @');
|
||||||
|
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '345', '345', '345', '345', '345']);
|
||||||
|
term.writeSync('\x1b[2 @');
|
||||||
|
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '5', '5', '5', '5', '5']);
|
||||||
|
});
|
||||||
|
it('SR (scrollRight)', () => {
|
||||||
|
term.writeSync('12345'.repeat(6));
|
||||||
|
term.writeSync('\x1b[ A');
|
||||||
|
assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1234', ' 1234', ' 1234', ' 1234', ' 1234']);
|
||||||
|
term.writeSync('\x1b[0 A');
|
||||||
|
assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 123', ' 123', ' 123', ' 123', ' 123']);
|
||||||
|
term.writeSync('\x1b[2 A');
|
||||||
|
assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1', ' 1', ' 1', ' 1', ' 1']);
|
||||||
|
});
|
||||||
|
it('insertColumns (DECIC)', () => {
|
||||||
|
term.writeSync('12345'.repeat(6));
|
||||||
|
term.writeSync('\x1b[3;3H');
|
||||||
|
term.writeSync('\x1b[\'}');
|
||||||
|
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
|
||||||
|
term.reset();
|
||||||
|
term.writeSync('12345'.repeat(6));
|
||||||
|
term.writeSync('\x1b[3;3H');
|
||||||
|
term.writeSync('\x1b[1\'}');
|
||||||
|
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
|
||||||
|
term.reset();
|
||||||
|
term.writeSync('12345'.repeat(6));
|
||||||
|
term.writeSync('\x1b[3;3H');
|
||||||
|
term.writeSync('\x1b[2\'}');
|
||||||
|
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 3', '12 3', '12 3', '12 3', '12 3']);
|
||||||
|
});
|
||||||
|
it('deleteColumns (DECDC)', () => {
|
||||||
|
term.writeSync('12345'.repeat(6));
|
||||||
|
term.writeSync('\x1b[3;3H');
|
||||||
|
term.writeSync('\x1b[\'~');
|
||||||
|
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']);
|
||||||
|
term.reset();
|
||||||
|
term.writeSync('12345'.repeat(6));
|
||||||
|
term.writeSync('\x1b[3;3H');
|
||||||
|
term.writeSync('\x1b[1\'~');
|
||||||
|
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']);
|
||||||
|
term.reset();
|
||||||
|
term.writeSync('12345'.repeat(6));
|
||||||
|
term.writeSync('\x1b[3;3H');
|
||||||
|
term.writeSync('\x1b[2\'~');
|
||||||
|
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '125', '125', '125', '125', '125']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should parse big chunks in smaller subchunks', () => {
|
||||||
|
// max single chunk size is hardcoded as 131072
|
||||||
|
const calls: any[] = [];
|
||||||
|
const term = new TestTerminal({cols: 10, rows: 10});
|
||||||
|
(term as any)._inputHandler._parser.parse = (data: Uint32Array, length: number) => {
|
||||||
|
calls.push([data.length, length]);
|
||||||
|
};
|
||||||
|
term.writeSync('12345');
|
||||||
|
term.writeSync('a'.repeat(10000));
|
||||||
|
term.writeSync('a'.repeat(200000));
|
||||||
|
term.writeSync('a'.repeat(300000));
|
||||||
|
assert.deepEqual(calls, [
|
||||||
|
[4096, 5],
|
||||||
|
[10000, 10000],
|
||||||
|
[131072, 131072], [131072, 200000 - 131072],
|
||||||
|
[131072, 131072], [131072, 131072], [131072, 300000 - 131072 - 131072]
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+138
-40
@@ -28,6 +28,11 @@ import { DcsHandler } from 'common/parser/DcsParser';
|
|||||||
*/
|
*/
|
||||||
const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2};
|
const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max length of the UTF32 input buffer. Real memory consumption is 4 times higher.
|
||||||
|
*/
|
||||||
|
const MAX_PARSEBUFFER_LENGTH = 131072;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DCS subparser implementations
|
* DCS subparser implementations
|
||||||
@@ -175,7 +180,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
* CSI handler
|
* CSI handler
|
||||||
*/
|
*/
|
||||||
this._parser.setCsiHandler({final: '@'}, params => this.insertChars(params));
|
this._parser.setCsiHandler({final: '@'}, params => this.insertChars(params));
|
||||||
|
this._parser.setCsiHandler({intermediates: ' ', final: '@'}, params => this.scrollLeft(params));
|
||||||
this._parser.setCsiHandler({final: 'A'}, params => this.cursorUp(params));
|
this._parser.setCsiHandler({final: 'A'}, params => this.cursorUp(params));
|
||||||
|
this._parser.setCsiHandler({intermediates: ' ', final: 'A'}, params => this.scrollRight(params));
|
||||||
this._parser.setCsiHandler({final: 'B'}, params => this.cursorDown(params));
|
this._parser.setCsiHandler({final: 'B'}, params => this.cursorDown(params));
|
||||||
this._parser.setCsiHandler({final: 'C'}, params => this.cursorForward(params));
|
this._parser.setCsiHandler({final: 'C'}, params => this.cursorForward(params));
|
||||||
this._parser.setCsiHandler({final: 'D'}, params => this.cursorBackward(params));
|
this._parser.setCsiHandler({final: 'D'}, params => this.cursorBackward(params));
|
||||||
@@ -216,6 +223,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
this._parser.setCsiHandler({final: 'r'}, params => this.setScrollRegion(params));
|
this._parser.setCsiHandler({final: 'r'}, params => this.setScrollRegion(params));
|
||||||
this._parser.setCsiHandler({final: 's'}, params => this.saveCursor(params));
|
this._parser.setCsiHandler({final: 's'}, params => this.saveCursor(params));
|
||||||
this._parser.setCsiHandler({final: 'u'}, params => this.restoreCursor(params));
|
this._parser.setCsiHandler({final: 'u'}, params => this.restoreCursor(params));
|
||||||
|
this._parser.setCsiHandler({intermediates: '\'', final: '}'}, params => this.insertColumns(params));
|
||||||
|
this._parser.setCsiHandler({intermediates: '\'', final: '~'}, params => this.deleteColumns(params));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* execute handler
|
* execute handler
|
||||||
@@ -324,40 +333,41 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public parse(data: string): void {
|
public parse(data: string | Uint8Array): void {
|
||||||
let buffer = this._bufferService.buffer;
|
let buffer = this._bufferService.buffer;
|
||||||
const cursorStartX = buffer.x;
|
const cursorStartX = buffer.x;
|
||||||
const cursorStartY = buffer.y;
|
const cursorStartY = buffer.y;
|
||||||
|
|
||||||
this._logService.debug('parsing data', data);
|
this._logService.debug('parsing data', data);
|
||||||
|
|
||||||
|
// resize input buffer if needed
|
||||||
if (this._parseBuffer.length < data.length) {
|
if (this._parseBuffer.length < data.length) {
|
||||||
this._parseBuffer = new Uint32Array(data.length);
|
if (this._parseBuffer.length < MAX_PARSEBUFFER_LENGTH) {
|
||||||
|
this._parseBuffer = new Uint32Array(Math.min(data.length, MAX_PARSEBUFFER_LENGTH));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process big data in smaller chunks
|
||||||
|
if (data.length > MAX_PARSEBUFFER_LENGTH) {
|
||||||
|
for (let i = 0; i < data.length; i += MAX_PARSEBUFFER_LENGTH) {
|
||||||
|
const end = i + MAX_PARSEBUFFER_LENGTH < data.length ? i + MAX_PARSEBUFFER_LENGTH : data.length;
|
||||||
|
const len = (typeof data === 'string')
|
||||||
|
? this._stringDecoder.decode(data.substring(i, end), this._parseBuffer)
|
||||||
|
: this._utf8Decoder.decode(data.subarray(i, end), this._parseBuffer);
|
||||||
|
this._parser.parse(this._parseBuffer, len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const len = (typeof data === 'string')
|
||||||
|
? this._stringDecoder.decode(data, this._parseBuffer)
|
||||||
|
: this._utf8Decoder.decode(data, this._parseBuffer);
|
||||||
|
this._parser.parse(this._parseBuffer, len);
|
||||||
}
|
}
|
||||||
this._parser.parse(this._parseBuffer, this._stringDecoder.decode(data, this._parseBuffer));
|
|
||||||
|
|
||||||
buffer = this._bufferService.buffer;
|
|
||||||
if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
|
|
||||||
this._onCursorMove.fire();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public parseUtf8(data: Uint8Array): void {
|
|
||||||
let buffer = this._bufferService.buffer;
|
|
||||||
const cursorStartX = buffer.x;
|
|
||||||
const cursorStartY = buffer.y;
|
|
||||||
|
|
||||||
this._logService.debug('parsing data', data);
|
|
||||||
|
|
||||||
if (this._parseBuffer.length < data.length) {
|
|
||||||
this._parseBuffer = new Uint32Array(data.length);
|
|
||||||
}
|
|
||||||
this._parser.parse(this._parseBuffer, this._utf8Decoder.decode(data, this._parseBuffer));
|
|
||||||
|
|
||||||
buffer = this._bufferService.buffer;
|
buffer = this._bufferService.buffer;
|
||||||
if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
|
if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
|
||||||
this._onCursorMove.fire();
|
this._onCursorMove.fire();
|
||||||
}
|
}
|
||||||
|
this._terminal.refresh(this._dirtyRowService.start, this._dirtyRowService.end);
|
||||||
}
|
}
|
||||||
|
|
||||||
public print(data: Uint32Array, start: number, end: number): void {
|
public print(data: Uint32Array, start: number, end: number): void {
|
||||||
@@ -1046,18 +1056,108 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
* CSI Ps T Scroll down Ps lines (default = 1) (SD).
|
* CSI Ps T Scroll down Ps lines (default = 1) (SD).
|
||||||
*/
|
*/
|
||||||
public scrollDown(params: IParams): void {
|
public scrollDown(params: IParams): void {
|
||||||
if (params.length < 2) {
|
let param = params.params[0] || 1;
|
||||||
let param = params.params[0] || 1;
|
|
||||||
|
|
||||||
// make buffer local for faster access
|
// make buffer local for faster access
|
||||||
const buffer = this._bufferService.buffer;
|
const buffer = this._bufferService.buffer;
|
||||||
|
|
||||||
while (param--) {
|
while (param--) {
|
||||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
|
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
|
||||||
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
|
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||||
}
|
|
||||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
|
||||||
}
|
}
|
||||||
|
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSI Ps SP @ Scroll left Ps columns (default = 1) (SL) ECMA-48
|
||||||
|
*
|
||||||
|
* Notation: (Pn)
|
||||||
|
* Representation: CSI Pn 02/00 04/00
|
||||||
|
* Parameter default value: Pn = 1
|
||||||
|
* SL causes the data in the presentation component to be moved by n character positions
|
||||||
|
* if the line orientation is horizontal, or by n line positions if the line orientation
|
||||||
|
* is vertical, such that the data appear to move to the left; where n equals the value of Pn.
|
||||||
|
* The active presentation position is not affected by this control function.
|
||||||
|
*
|
||||||
|
* Supported:
|
||||||
|
* - always left shift (no line orientation setting respected)
|
||||||
|
*/
|
||||||
|
public scrollLeft(params: IParams): void {
|
||||||
|
const buffer = this._bufferService.buffer;
|
||||||
|
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const param = params.params[0] || 1;
|
||||||
|
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
|
||||||
|
const line = buffer.lines.get(buffer.ybase + y);
|
||||||
|
line.deleteCells(0, param, buffer.getNullCell(this._terminal.eraseAttrData()));
|
||||||
|
line.isWrapped = false;
|
||||||
|
}
|
||||||
|
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSI Ps SP A Scroll right Ps columns (default = 1) (SR) ECMA-48
|
||||||
|
*
|
||||||
|
* Notation: (Pn)
|
||||||
|
* Representation: CSI Pn 02/00 04/01
|
||||||
|
* Parameter default value: Pn = 1
|
||||||
|
* SR causes the data in the presentation component to be moved by n character positions
|
||||||
|
* if the line orientation is horizontal, or by n line positions if the line orientation
|
||||||
|
* is vertical, such that the data appear to move to the right; where n equals the value of Pn.
|
||||||
|
* The active presentation position is not affected by this control function.
|
||||||
|
*
|
||||||
|
* Supported:
|
||||||
|
* - always right shift (no line orientation setting respected)
|
||||||
|
*/
|
||||||
|
public scrollRight(params: IParams): void {
|
||||||
|
const buffer = this._bufferService.buffer;
|
||||||
|
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const param = params.params[0] || 1;
|
||||||
|
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
|
||||||
|
const line = buffer.lines.get(buffer.ybase + y);
|
||||||
|
line.insertCells(0, param, buffer.getNullCell(this._terminal.eraseAttrData()));
|
||||||
|
line.isWrapped = false;
|
||||||
|
}
|
||||||
|
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSI Pm ' }
|
||||||
|
* Insert Ps Column(s) (default = 1) (DECIC), VT420 and up.
|
||||||
|
*/
|
||||||
|
public insertColumns(params: IParams): void {
|
||||||
|
const buffer = this._bufferService.buffer;
|
||||||
|
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const param = params.params[0] || 1;
|
||||||
|
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
|
||||||
|
const line = this._bufferService.buffer.lines.get(buffer.ybase + y);
|
||||||
|
line.insertCells(buffer.x, param, buffer.getNullCell(this._terminal.eraseAttrData()));
|
||||||
|
line.isWrapped = false;
|
||||||
|
}
|
||||||
|
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSI Pm ' ~
|
||||||
|
* Delete Ps Column(s) (default = 1) (DECDC), VT420 and up.
|
||||||
|
*/
|
||||||
|
public deleteColumns(params: IParams): void {
|
||||||
|
const buffer = this._bufferService.buffer;
|
||||||
|
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const param = params.params[0] || 1;
|
||||||
|
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
|
||||||
|
const line = buffer.lines.get(buffer.ybase + y);
|
||||||
|
line.deleteCells(buffer.x, param, buffer.getNullCell(this._terminal.eraseAttrData()));
|
||||||
|
line.isWrapped = false;
|
||||||
|
}
|
||||||
|
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1334,16 +1434,14 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
// focusout: ^[[O
|
// focusout: ^[[O
|
||||||
this._terminal.sendFocus = true;
|
this._terminal.sendFocus = true;
|
||||||
break;
|
break;
|
||||||
case 1005: // utf8 ext mode mouse
|
case 1005: // utf8 ext mode mouse - removed in #2507
|
||||||
// for wide terminals
|
this._logService.debug('DECSET 1005 not supported (see #2507)');
|
||||||
// simply encodes large values as utf8 characters
|
|
||||||
this._coreMouseService.activeEncoding = 'UTF8';
|
|
||||||
break;
|
break;
|
||||||
case 1006: // sgr ext mode mouse
|
case 1006: // sgr ext mode mouse
|
||||||
this._coreMouseService.activeEncoding = 'SGR';
|
this._coreMouseService.activeEncoding = 'SGR';
|
||||||
break;
|
break;
|
||||||
case 1015: // urxvt ext mode mouse
|
case 1015: // urxvt ext mode mouse - removed in #2507
|
||||||
this._coreMouseService.activeEncoding = 'URXVT';
|
this._logService.debug('DECSET 1015 not supported (see #2507)');
|
||||||
break;
|
break;
|
||||||
case 25: // show cursor
|
case 25: // show cursor
|
||||||
this._terminal.cursorHidden = false;
|
this._terminal.cursorHidden = false;
|
||||||
@@ -1507,14 +1605,14 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
case 1004: // send focusin/focusout events
|
case 1004: // send focusin/focusout events
|
||||||
this._terminal.sendFocus = false;
|
this._terminal.sendFocus = false;
|
||||||
break;
|
break;
|
||||||
case 1005: // utf8 ext mode mouse
|
case 1005: // utf8 ext mode mouse - removed in #2507
|
||||||
this._coreMouseService.activeEncoding = 'DEFAULT';
|
this._logService.debug('DECRST 1005 not supported (see #2507)');
|
||||||
break;
|
break;
|
||||||
case 1006: // sgr ext mode mouse
|
case 1006: // sgr ext mode mouse
|
||||||
this._coreMouseService.activeEncoding = 'DEFAULT';
|
this._coreMouseService.activeEncoding = 'DEFAULT';
|
||||||
break;
|
break;
|
||||||
case 1015: // urxvt ext mode mouse
|
case 1015: // urxvt ext mode mouse - removed in #2507
|
||||||
this._coreMouseService.activeEncoding = 'DEFAULT';
|
this._logService.debug('DECRST 1015 not supported (see #2507)');
|
||||||
break;
|
break;
|
||||||
case 25: // hide cursor
|
case 25: // hide cursor
|
||||||
this._terminal.cursorHidden = true;
|
this._terminal.cursorHidden = true;
|
||||||
|
|||||||
+37
-42
@@ -29,11 +29,6 @@ describe('Terminal', () => {
|
|||||||
(<any>term).renderer = new MockRenderer();
|
(<any>term).renderer = new MockRenderer();
|
||||||
term.viewport = new MockViewport();
|
term.viewport = new MockViewport();
|
||||||
(<any>term)._compositionHelper = new MockCompositionHelper();
|
(<any>term)._compositionHelper = new MockCompositionHelper();
|
||||||
// Force synchronous writes
|
|
||||||
term.write = (data) => {
|
|
||||||
term.writeBuffer.push(data);
|
|
||||||
(<any>term)._innerWrite();
|
|
||||||
};
|
|
||||||
(<any>term).element = {
|
(<any>term).element = {
|
||||||
classList: {
|
classList: {
|
||||||
toggle: () => { },
|
toggle: () => { },
|
||||||
@@ -59,18 +54,18 @@ describe('Terminal', () => {
|
|||||||
// });
|
// });
|
||||||
it('should fire the onCursorMove event', (done) => {
|
it('should fire the onCursorMove event', (done) => {
|
||||||
term.onCursorMove(() => done());
|
term.onCursorMove(() => done());
|
||||||
term.write('foo');
|
term.writeSync('foo');
|
||||||
});
|
});
|
||||||
it('should fire the onLineFeed event', (done) => {
|
it('should fire the onLineFeed event', (done) => {
|
||||||
term.onLineFeed(() => done());
|
term.onLineFeed(() => done());
|
||||||
term.write('\n');
|
term.writeSync('\n');
|
||||||
});
|
});
|
||||||
it('should fire a scroll event when scrollback is created', (done) => {
|
it('should fire a scroll event when scrollback is created', (done) => {
|
||||||
term.onScroll(() => done());
|
term.onScroll(() => done());
|
||||||
term.write('\n'.repeat(INIT_ROWS));
|
term.writeSync('\n'.repeat(INIT_ROWS));
|
||||||
});
|
});
|
||||||
it('should fire a scroll event when scrollback is cleared', (done) => {
|
it('should fire a scroll event when scrollback is cleared', (done) => {
|
||||||
term.write('\n'.repeat(INIT_ROWS));
|
term.writeSync('\n'.repeat(INIT_ROWS));
|
||||||
term.onScroll(() => done());
|
term.onScroll(() => done());
|
||||||
term.clear();
|
term.clear();
|
||||||
});
|
});
|
||||||
@@ -195,7 +190,7 @@ describe('Terminal', () => {
|
|||||||
it('should clear a buffer larger than rows', () => {
|
it('should clear a buffer larger than rows', () => {
|
||||||
// Fill the buffer with dummy rows
|
// Fill the buffer with dummy rows
|
||||||
for (let i = 0; i < term.rows * 2; i++) {
|
for (let i = 0; i < term.rows * 2; i++) {
|
||||||
term.write('test\n');
|
term.writeSync('test\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
const promptLine = term.buffer.lines.get(term.buffer.ybase + term.buffer.y);
|
const promptLine = term.buffer.lines.get(term.buffer.ybase + term.buffer.y);
|
||||||
@@ -244,7 +239,7 @@ describe('Terminal', () => {
|
|||||||
assert.equal(e, '\x1b[200~foo\x1b[201~');
|
assert.equal(e, '\x1b[200~foo\x1b[201~');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
term.write('\x1b[?2004h');
|
term.writeSync('\x1b[?2004h');
|
||||||
term.paste('foo');
|
term.paste('foo');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -254,7 +249,7 @@ describe('Terminal', () => {
|
|||||||
let startYDisp: number;
|
let startYDisp: number;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
for (let i = 0; i < INIT_ROWS * 2; i++) {
|
for (let i = 0; i < INIT_ROWS * 2; i++) {
|
||||||
term.writeln('test');
|
term.writeSync('test\r\n');
|
||||||
}
|
}
|
||||||
startYDisp = INIT_ROWS + 1;
|
startYDisp = INIT_ROWS + 1;
|
||||||
});
|
});
|
||||||
@@ -289,7 +284,7 @@ describe('Terminal', () => {
|
|||||||
let startYDisp: number;
|
let startYDisp: number;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
for (let i = 0; i < term.rows * 3; i++) {
|
for (let i = 0; i < term.rows * 3; i++) {
|
||||||
term.writeln('test');
|
term.writeSync('test\r\n');
|
||||||
}
|
}
|
||||||
startYDisp = (term.rows * 2) + 1;
|
startYDisp = (term.rows * 2) + 1;
|
||||||
});
|
});
|
||||||
@@ -312,7 +307,7 @@ describe('Terminal', () => {
|
|||||||
describe('scrollToTop', () => {
|
describe('scrollToTop', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
for (let i = 0; i < term.rows * 3; i++) {
|
for (let i = 0; i < term.rows * 3; i++) {
|
||||||
term.writeln('test');
|
term.writeSync('test\r\n');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
it('should scroll to the top', () => {
|
it('should scroll to the top', () => {
|
||||||
@@ -326,7 +321,7 @@ describe('Terminal', () => {
|
|||||||
let startYDisp: number;
|
let startYDisp: number;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
for (let i = 0; i < term.rows * 3; i++) {
|
for (let i = 0; i < term.rows * 3; i++) {
|
||||||
term.writeln('test');
|
term.writeSync('test\r\n');
|
||||||
}
|
}
|
||||||
startYDisp = (term.rows * 2) + 1;
|
startYDisp = (term.rows * 2) + 1;
|
||||||
});
|
});
|
||||||
@@ -347,7 +342,7 @@ describe('Terminal', () => {
|
|||||||
let startYDisp: number;
|
let startYDisp: number;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
for (let i = 0; i < term.rows * 3; i++) {
|
for (let i = 0; i < term.rows * 3; i++) {
|
||||||
term.writeln('test');
|
term.writeSync('test\r\n');
|
||||||
}
|
}
|
||||||
startYDisp = (term.rows * 2) + 1;
|
startYDisp = (term.rows * 2) + 1;
|
||||||
});
|
});
|
||||||
@@ -392,7 +387,7 @@ describe('Terminal', () => {
|
|||||||
it('should not scroll down, when a custom keydown handler prevents the event', () => {
|
it('should not scroll down, when a custom keydown handler prevents the event', () => {
|
||||||
// Add some output to the terminal
|
// Add some output to the terminal
|
||||||
for (let i = 0; i < term.rows * 3; i++) {
|
for (let i = 0; i < term.rows * 3; i++) {
|
||||||
term.writeln('test');
|
term.writeSync('test\r\n');
|
||||||
}
|
}
|
||||||
const startYDisp = (term.rows * 2) + 1;
|
const startYDisp = (term.rows * 2) + 1;
|
||||||
term.attachCustomKeyEventHandler(() => {
|
term.attachCustomKeyEventHandler(() => {
|
||||||
@@ -737,7 +732,7 @@ describe('Terminal', () => {
|
|||||||
const high = String.fromCharCode(0xD800);
|
const high = String.fromCharCode(0xD800);
|
||||||
const cell = new CellData();
|
const cell = new CellData();
|
||||||
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
||||||
term.write(high + String.fromCharCode(i));
|
term.writeSync(high + String.fromCharCode(i));
|
||||||
const tchar = term.buffer.lines.get(0).loadCell(0, cell);
|
const tchar = term.buffer.lines.get(0).loadCell(0, cell);
|
||||||
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
|
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
|
||||||
expect(tchar.getChars().length).eql(2);
|
expect(tchar.getChars().length).eql(2);
|
||||||
@@ -751,7 +746,7 @@ describe('Terminal', () => {
|
|||||||
const cell = new CellData();
|
const cell = new CellData();
|
||||||
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
||||||
term.buffer.x = term.cols - 1;
|
term.buffer.x = term.cols - 1;
|
||||||
term.write(high + String.fromCharCode(i));
|
term.writeSync(high + String.fromCharCode(i));
|
||||||
expect(term.buffer.lines.get(0).loadCell(term.buffer.x - 1, cell).getChars()).eql(high + String.fromCharCode(i));
|
expect(term.buffer.lines.get(0).loadCell(term.buffer.x - 1, cell).getChars()).eql(high + String.fromCharCode(i));
|
||||||
expect(term.buffer.lines.get(0).loadCell(term.buffer.x - 1, cell).getChars().length).eql(2);
|
expect(term.buffer.lines.get(0).loadCell(term.buffer.x - 1, cell).getChars().length).eql(2);
|
||||||
expect(term.buffer.lines.get(1).loadCell(0, cell).getChars()).eql('');
|
expect(term.buffer.lines.get(1).loadCell(0, cell).getChars()).eql('');
|
||||||
@@ -764,7 +759,7 @@ describe('Terminal', () => {
|
|||||||
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
||||||
term.buffer.x = term.cols - 1;
|
term.buffer.x = term.cols - 1;
|
||||||
term.wraparoundMode = true;
|
term.wraparoundMode = true;
|
||||||
term.write('a' + high + String.fromCharCode(i));
|
term.writeSync('a' + high + String.fromCharCode(i));
|
||||||
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars()).eql('a');
|
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars()).eql('a');
|
||||||
expect(term.buffer.lines.get(1).loadCell(0, cell).getChars()).eql(high + String.fromCharCode(i));
|
expect(term.buffer.lines.get(1).loadCell(0, cell).getChars()).eql(high + String.fromCharCode(i));
|
||||||
expect(term.buffer.lines.get(1).loadCell(0, cell).getChars().length).eql(2);
|
expect(term.buffer.lines.get(1).loadCell(0, cell).getChars().length).eql(2);
|
||||||
@@ -782,7 +777,7 @@ describe('Terminal', () => {
|
|||||||
if (width !== 1) {
|
if (width !== 1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
term.write('a' + high + String.fromCharCode(i));
|
term.writeSync('a' + high + String.fromCharCode(i));
|
||||||
// auto wraparound mode should cut off the rest of the line
|
// auto wraparound mode should cut off the rest of the line
|
||||||
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars()).eql(high + String.fromCharCode(i));
|
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars()).eql(high + String.fromCharCode(i));
|
||||||
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars().length).eql(2);
|
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars().length).eql(2);
|
||||||
@@ -794,8 +789,8 @@ describe('Terminal', () => {
|
|||||||
const high = String.fromCharCode(0xD800);
|
const high = String.fromCharCode(0xD800);
|
||||||
const cell = new CellData();
|
const cell = new CellData();
|
||||||
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
|
||||||
term.write(high);
|
term.writeSync(high);
|
||||||
term.write(String.fromCharCode(i));
|
term.writeSync(String.fromCharCode(i));
|
||||||
const tchar = term.buffer.lines.get(0).loadCell(0, cell);
|
const tchar = term.buffer.lines.get(0).loadCell(0, cell);
|
||||||
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
|
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
|
||||||
expect(tchar.getChars().length).eql(2);
|
expect(tchar.getChars().length).eql(2);
|
||||||
@@ -809,7 +804,7 @@ describe('Terminal', () => {
|
|||||||
describe('unicode - combining characters', () => {
|
describe('unicode - combining characters', () => {
|
||||||
const cell = new CellData();
|
const cell = new CellData();
|
||||||
it('café', () => {
|
it('café', () => {
|
||||||
term.write('cafe\u0301');
|
term.writeSync('cafe\u0301');
|
||||||
term.buffer.lines.get(0).loadCell(3, cell);
|
term.buffer.lines.get(0).loadCell(3, cell);
|
||||||
expect(cell.getChars()).eql('e\u0301');
|
expect(cell.getChars()).eql('e\u0301');
|
||||||
expect(cell.getChars().length).eql(2);
|
expect(cell.getChars().length).eql(2);
|
||||||
@@ -817,7 +812,7 @@ describe('Terminal', () => {
|
|||||||
});
|
});
|
||||||
it('café - end of line', () => {
|
it('café - end of line', () => {
|
||||||
term.buffer.x = term.cols - 1 - 3;
|
term.buffer.x = term.cols - 1 - 3;
|
||||||
term.write('cafe\u0301');
|
term.writeSync('cafe\u0301');
|
||||||
term.buffer.lines.get(0).loadCell(term.cols - 1, cell);
|
term.buffer.lines.get(0).loadCell(term.cols - 1, cell);
|
||||||
expect(cell.getChars()).eql('e\u0301');
|
expect(cell.getChars()).eql('e\u0301');
|
||||||
expect(cell.getChars().length).eql(2);
|
expect(cell.getChars().length).eql(2);
|
||||||
@@ -829,7 +824,7 @@ describe('Terminal', () => {
|
|||||||
});
|
});
|
||||||
it('multiple combined é', () => {
|
it('multiple combined é', () => {
|
||||||
term.wraparoundMode = true;
|
term.wraparoundMode = true;
|
||||||
term.write(Array(100).join('e\u0301'));
|
term.writeSync(Array(100).join('e\u0301'));
|
||||||
for (let i = 0; i < term.cols; ++i) {
|
for (let i = 0; i < term.cols; ++i) {
|
||||||
term.buffer.lines.get(0).loadCell(i, cell);
|
term.buffer.lines.get(0).loadCell(i, cell);
|
||||||
expect(cell.getChars()).eql('e\u0301');
|
expect(cell.getChars()).eql('e\u0301');
|
||||||
@@ -843,7 +838,7 @@ describe('Terminal', () => {
|
|||||||
});
|
});
|
||||||
it('multiple surrogate with combined', () => {
|
it('multiple surrogate with combined', () => {
|
||||||
term.wraparoundMode = true;
|
term.wraparoundMode = true;
|
||||||
term.write(Array(100).join('\uD800\uDC00\u0301'));
|
term.writeSync(Array(100).join('\uD800\uDC00\u0301'));
|
||||||
for (let i = 0; i < term.cols; ++i) {
|
for (let i = 0; i < term.cols; ++i) {
|
||||||
term.buffer.lines.get(0).loadCell(i, cell);
|
term.buffer.lines.get(0).loadCell(i, cell);
|
||||||
expect(cell.getChars()).eql('\uD800\uDC00\u0301');
|
expect(cell.getChars()).eql('\uD800\uDC00\u0301');
|
||||||
@@ -861,18 +856,18 @@ describe('Terminal', () => {
|
|||||||
const cell = new CellData();
|
const cell = new CellData();
|
||||||
it('cursor movement even', () => {
|
it('cursor movement even', () => {
|
||||||
expect(term.buffer.x).eql(0);
|
expect(term.buffer.x).eql(0);
|
||||||
term.write('¥');
|
term.writeSync('¥');
|
||||||
expect(term.buffer.x).eql(2);
|
expect(term.buffer.x).eql(2);
|
||||||
});
|
});
|
||||||
it('cursor movement odd', () => {
|
it('cursor movement odd', () => {
|
||||||
term.buffer.x = 1;
|
term.buffer.x = 1;
|
||||||
expect(term.buffer.x).eql(1);
|
expect(term.buffer.x).eql(1);
|
||||||
term.write('¥');
|
term.writeSync('¥');
|
||||||
expect(term.buffer.x).eql(3);
|
expect(term.buffer.x).eql(3);
|
||||||
});
|
});
|
||||||
it('line of ¥ even', () => {
|
it('line of ¥ even', () => {
|
||||||
term.wraparoundMode = true;
|
term.wraparoundMode = true;
|
||||||
term.write(Array(50).join('¥'));
|
term.writeSync(Array(50).join('¥'));
|
||||||
for (let i = 0; i < term.cols; ++i) {
|
for (let i = 0; i < term.cols; ++i) {
|
||||||
term.buffer.lines.get(0).loadCell(i, cell);
|
term.buffer.lines.get(0).loadCell(i, cell);
|
||||||
if (i % 2) {
|
if (i % 2) {
|
||||||
@@ -893,7 +888,7 @@ describe('Terminal', () => {
|
|||||||
it('line of ¥ odd', () => {
|
it('line of ¥ odd', () => {
|
||||||
term.wraparoundMode = true;
|
term.wraparoundMode = true;
|
||||||
term.buffer.x = 1;
|
term.buffer.x = 1;
|
||||||
term.write(Array(50).join('¥'));
|
term.writeSync(Array(50).join('¥'));
|
||||||
for (let i = 1; i < term.cols - 1; ++i) {
|
for (let i = 1; i < term.cols - 1; ++i) {
|
||||||
term.buffer.lines.get(0).loadCell(i, cell);
|
term.buffer.lines.get(0).loadCell(i, cell);
|
||||||
if (!(i % 2)) {
|
if (!(i % 2)) {
|
||||||
@@ -918,7 +913,7 @@ describe('Terminal', () => {
|
|||||||
it('line of ¥ with combining odd', () => {
|
it('line of ¥ with combining odd', () => {
|
||||||
term.wraparoundMode = true;
|
term.wraparoundMode = true;
|
||||||
term.buffer.x = 1;
|
term.buffer.x = 1;
|
||||||
term.write(Array(50).join('¥\u0301'));
|
term.writeSync(Array(50).join('¥\u0301'));
|
||||||
for (let i = 1; i < term.cols - 1; ++i) {
|
for (let i = 1; i < term.cols - 1; ++i) {
|
||||||
term.buffer.lines.get(0).loadCell(i, cell);
|
term.buffer.lines.get(0).loadCell(i, cell);
|
||||||
if (!(i % 2)) {
|
if (!(i % 2)) {
|
||||||
@@ -942,7 +937,7 @@ describe('Terminal', () => {
|
|||||||
});
|
});
|
||||||
it('line of ¥ with combining even', () => {
|
it('line of ¥ with combining even', () => {
|
||||||
term.wraparoundMode = true;
|
term.wraparoundMode = true;
|
||||||
term.write(Array(50).join('¥\u0301'));
|
term.writeSync(Array(50).join('¥\u0301'));
|
||||||
for (let i = 0; i < term.cols; ++i) {
|
for (let i = 0; i < term.cols; ++i) {
|
||||||
term.buffer.lines.get(0).loadCell(i, cell);
|
term.buffer.lines.get(0).loadCell(i, cell);
|
||||||
if (i % 2) {
|
if (i % 2) {
|
||||||
@@ -963,7 +958,7 @@ describe('Terminal', () => {
|
|||||||
it('line of surrogate fullwidth with combining odd', () => {
|
it('line of surrogate fullwidth with combining odd', () => {
|
||||||
term.wraparoundMode = true;
|
term.wraparoundMode = true;
|
||||||
term.buffer.x = 1;
|
term.buffer.x = 1;
|
||||||
term.write(Array(50).join('\ud843\ude6d\u0301'));
|
term.writeSync(Array(50).join('\ud843\ude6d\u0301'));
|
||||||
for (let i = 1; i < term.cols - 1; ++i) {
|
for (let i = 1; i < term.cols - 1; ++i) {
|
||||||
term.buffer.lines.get(0).loadCell(i, cell);
|
term.buffer.lines.get(0).loadCell(i, cell);
|
||||||
if (!(i % 2)) {
|
if (!(i % 2)) {
|
||||||
@@ -987,7 +982,7 @@ describe('Terminal', () => {
|
|||||||
});
|
});
|
||||||
it('line of surrogate fullwidth with combining even', () => {
|
it('line of surrogate fullwidth with combining even', () => {
|
||||||
term.wraparoundMode = true;
|
term.wraparoundMode = true;
|
||||||
term.write(Array(50).join('\ud843\ude6d\u0301'));
|
term.writeSync(Array(50).join('\ud843\ude6d\u0301'));
|
||||||
for (let i = 0; i < term.cols; ++i) {
|
for (let i = 0; i < term.cols; ++i) {
|
||||||
term.buffer.lines.get(0).loadCell(i, cell);
|
term.buffer.lines.get(0).loadCell(i, cell);
|
||||||
if (i % 2) {
|
if (i % 2) {
|
||||||
@@ -1010,11 +1005,11 @@ describe('Terminal', () => {
|
|||||||
describe('insert mode', () => {
|
describe('insert mode', () => {
|
||||||
const cell = new CellData();
|
const cell = new CellData();
|
||||||
it('halfwidth - all', () => {
|
it('halfwidth - all', () => {
|
||||||
term.write(Array(9).join('0123456789').slice(-80));
|
term.writeSync(Array(9).join('0123456789').slice(-80));
|
||||||
term.buffer.x = 10;
|
term.buffer.x = 10;
|
||||||
term.buffer.y = 0;
|
term.buffer.y = 0;
|
||||||
term.insertMode = true;
|
term.insertMode = true;
|
||||||
term.write('abcde');
|
term.writeSync('abcde');
|
||||||
expect(term.buffer.lines.get(0).length).eql(term.cols);
|
expect(term.buffer.lines.get(0).length).eql(term.cols);
|
||||||
expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('a');
|
expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('a');
|
||||||
expect(term.buffer.lines.get(0).loadCell(14, cell).getChars()).eql('e');
|
expect(term.buffer.lines.get(0).loadCell(14, cell).getChars()).eql('e');
|
||||||
@@ -1022,11 +1017,11 @@ describe('Terminal', () => {
|
|||||||
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql('4');
|
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql('4');
|
||||||
});
|
});
|
||||||
it('fullwidth - insert', () => {
|
it('fullwidth - insert', () => {
|
||||||
term.write(Array(9).join('0123456789').slice(-80));
|
term.writeSync(Array(9).join('0123456789').slice(-80));
|
||||||
term.buffer.x = 10;
|
term.buffer.x = 10;
|
||||||
term.buffer.y = 0;
|
term.buffer.y = 0;
|
||||||
term.insertMode = true;
|
term.insertMode = true;
|
||||||
term.write('¥¥¥');
|
term.writeSync('¥¥¥');
|
||||||
expect(term.buffer.lines.get(0).length).eql(term.cols);
|
expect(term.buffer.lines.get(0).length).eql(term.cols);
|
||||||
expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('¥');
|
expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('¥');
|
||||||
expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('');
|
expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('');
|
||||||
@@ -1035,16 +1030,16 @@ describe('Terminal', () => {
|
|||||||
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql('3');
|
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql('3');
|
||||||
});
|
});
|
||||||
it('fullwidth - right border', () => {
|
it('fullwidth - right border', () => {
|
||||||
term.write(Array(41).join('¥'));
|
term.writeSync(Array(41).join('¥'));
|
||||||
term.buffer.x = 10;
|
term.buffer.x = 10;
|
||||||
term.buffer.y = 0;
|
term.buffer.y = 0;
|
||||||
term.insertMode = true;
|
term.insertMode = true;
|
||||||
term.write('a');
|
term.writeSync('a');
|
||||||
expect(term.buffer.lines.get(0).length).eql(term.cols);
|
expect(term.buffer.lines.get(0).length).eql(term.cols);
|
||||||
expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('a');
|
expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('a');
|
||||||
expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('¥');
|
expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('¥');
|
||||||
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql(''); // fullwidth char got replaced
|
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql(''); // fullwidth char got replaced
|
||||||
term.write('b');
|
term.writeSync('b');
|
||||||
expect(term.buffer.lines.get(0).length).eql(term.cols);
|
expect(term.buffer.lines.get(0).length).eql(term.cols);
|
||||||
expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('b');
|
expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('b');
|
||||||
expect(term.buffer.lines.get(0).loadCell(12, cell).getChars()).eql('¥');
|
expect(term.buffer.lines.get(0).loadCell(12, cell).getChars()).eql('¥');
|
||||||
|
|||||||
+24
-222
@@ -62,25 +62,11 @@ import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions,
|
|||||||
import { DirtyRowService } from 'common/services/DirtyRowService';
|
import { DirtyRowService } from 'common/services/DirtyRowService';
|
||||||
import { InstantiationService } from 'common/services/InstantiationService';
|
import { InstantiationService } from 'common/services/InstantiationService';
|
||||||
import { CoreMouseService } from 'common/services/CoreMouseService';
|
import { CoreMouseService } from 'common/services/CoreMouseService';
|
||||||
|
import { WriteBuffer } from 'common/input/WriteBuffer';
|
||||||
|
|
||||||
// Let it work inside Node.js for automated testing purposes.
|
// Let it work inside Node.js for automated testing purposes.
|
||||||
const document = (typeof window !== 'undefined') ? window.document : null;
|
const document = (typeof window !== 'undefined') ? window.document : null;
|
||||||
|
|
||||||
/**
|
|
||||||
* The amount of write requests to queue before sending an XOFF signal to the
|
|
||||||
* pty process. This number must be small in order for ^C and similar sequences
|
|
||||||
* to be responsive.
|
|
||||||
*/
|
|
||||||
const WRITE_BUFFER_PAUSE_THRESHOLD = 5;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The max number of ms to spend on writes before allowing the renderer to
|
|
||||||
* catch up with a 0ms setTimeout. A value of < 33 to keep us close to
|
|
||||||
* 30fps, and a value of < 16 to try to run at 60fps. Of course, the real FPS
|
|
||||||
* depends on the time it takes for the renderer to draw the frame.
|
|
||||||
*/
|
|
||||||
const WRITE_TIMEOUT_MS = 12;
|
|
||||||
const WRITE_BUFFER_LENGTH_THRESHOLD = 50;
|
|
||||||
|
|
||||||
export class Terminal extends Disposable implements ITerminal, IDisposable, IInputHandlingTerminal {
|
export class Terminal extends Disposable implements ITerminal, IDisposable, IInputHandlingTerminal {
|
||||||
public textarea: HTMLTextAreaElement;
|
public textarea: HTMLTextAreaElement;
|
||||||
@@ -90,7 +76,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
/**
|
/**
|
||||||
* The HTMLElement that the terminal is created in, set by Terminal.open.
|
* The HTMLElement that the terminal is created in, set by Terminal.open.
|
||||||
*/
|
*/
|
||||||
private _parent: HTMLElement;
|
private _parent: HTMLElement | null;
|
||||||
private _document: Document;
|
private _document: Document;
|
||||||
private _viewportScrollArea: HTMLElement;
|
private _viewportScrollArea: HTMLElement;
|
||||||
private _viewportElement: HTMLElement;
|
private _viewportElement: HTMLElement;
|
||||||
@@ -153,21 +139,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
public params: (string | number)[];
|
public params: (string | number)[];
|
||||||
public currentParam: string | number;
|
public currentParam: string | number;
|
||||||
|
|
||||||
// user input states
|
// write buffer
|
||||||
public writeBuffer: string[];
|
private _writeBuffer: WriteBuffer;
|
||||||
public writeBufferUtf8: Uint8Array[];
|
|
||||||
private _writeInProgress: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether _xterm.js_ sent XOFF in order to catch up with the pty process.
|
|
||||||
* This is a distinct state from writeStopped so that if the user requested
|
|
||||||
* XOFF via ^S that it will not automatically resume when the writeBuffer goes
|
|
||||||
* below threshold.
|
|
||||||
*/
|
|
||||||
private _xoffSentToCatchUp: boolean;
|
|
||||||
|
|
||||||
/** Whether writing has been stopped as a result of XOFF */
|
|
||||||
// private _writeStopped: boolean;
|
|
||||||
|
|
||||||
// Store if user went browsing history in scrollback
|
// Store if user went browsing history in scrollback
|
||||||
private _userScrolling: boolean;
|
private _userScrolling: boolean;
|
||||||
@@ -258,6 +231,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
|
|
||||||
this._setupOptionsListeners();
|
this._setupOptionsListeners();
|
||||||
this._setup();
|
this._setup();
|
||||||
|
|
||||||
|
this._writeBuffer = new WriteBuffer(data => this._inputHandler.parse(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
@@ -306,13 +281,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
this.params = [];
|
this.params = [];
|
||||||
this.currentParam = 0;
|
this.currentParam = 0;
|
||||||
|
|
||||||
// user input states
|
|
||||||
this.writeBuffer = [];
|
|
||||||
this.writeBufferUtf8 = [];
|
|
||||||
this._writeInProgress = false;
|
|
||||||
|
|
||||||
this._xoffSentToCatchUp = false;
|
|
||||||
// this._writeStopped = false;
|
|
||||||
this._userScrolling = false;
|
this._userScrolling = false;
|
||||||
|
|
||||||
// Register input handler and refire/handle events
|
// Register input handler and refire/handle events
|
||||||
@@ -415,18 +383,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
case 'theme':
|
case 'theme':
|
||||||
this._setTheme(this.optionsService.options.theme);
|
this._setTheme(this.optionsService.options.theme);
|
||||||
break;
|
break;
|
||||||
case 'scrollback':
|
|
||||||
const newBufferLength = this.rows + this.optionsService.options.scrollback;
|
|
||||||
if (this.buffer.lines.length > newBufferLength) {
|
|
||||||
const amountToTrim = this.buffer.lines.length - newBufferLength;
|
|
||||||
const needsRefresh = (this.buffer.ydisp - amountToTrim < 0);
|
|
||||||
this.buffer.lines.trimStart(amountToTrim);
|
|
||||||
this.buffer.ybase = Math.max(this.buffer.ybase - amountToTrim, 0);
|
|
||||||
this.buffer.ydisp = Math.max(this.buffer.ydisp - amountToTrim, 0);
|
|
||||||
if (needsRefresh) {
|
|
||||||
this.refresh(0, this.rows - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'windowsMode':
|
case 'windowsMode':
|
||||||
if (this.optionsService.options.windowsMode) {
|
if (this.optionsService.options.windowsMode) {
|
||||||
if (!this._windowsMode) {
|
if (!this._windowsMode) {
|
||||||
@@ -552,6 +508,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
throw new Error('Terminal requires a parent element.');
|
throw new Error('Terminal requires a parent element.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!document.body.contains(parent)) {
|
||||||
|
this._logService.warn('Terminal.open was called on an element that was not attached to the DOM');
|
||||||
|
}
|
||||||
|
|
||||||
this._document = this._parent.ownerDocument;
|
this._document = this._parent.ownerDocument;
|
||||||
|
|
||||||
// Create main element container
|
// Create main element container
|
||||||
@@ -604,8 +564,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
// Performance: Add viewport and helper elements from the fragment
|
// Performance: Add viewport and helper elements from the fragment
|
||||||
this.element.appendChild(fragment);
|
this.element.appendChild(fragment);
|
||||||
|
|
||||||
this._theme = this.options.theme;
|
this._theme = this.options.theme || this._theme;
|
||||||
this.options.theme = null;
|
this.options.theme = undefined;
|
||||||
this._colorManager = new ColorManager(document, this.options.allowTransparency);
|
this._colorManager = new ColorManager(document, this.options.allowTransparency);
|
||||||
this._colorManager.setTheme(this._theme);
|
this._colorManager.setTheme(this._theme);
|
||||||
|
|
||||||
@@ -1144,168 +1104,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes raw utf8 bytes to the terminal.
|
|
||||||
* @param data UintArray with UTF8 bytes to write to the terminal.
|
|
||||||
*/
|
|
||||||
public writeUtf8(data: Uint8Array): void {
|
|
||||||
// Ensure the terminal isn't disposed
|
|
||||||
if (this._isDisposed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore falsy data values
|
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.writeBufferUtf8.push(data);
|
|
||||||
|
|
||||||
// Send XOFF to pause the pty process if the write buffer becomes too large so
|
|
||||||
// xterm.js can catch up before more data is sent. This is necessary in order
|
|
||||||
// to keep signals such as ^C responsive.
|
|
||||||
if (this.options.useFlowControl && !this._xoffSentToCatchUp && this.writeBufferUtf8.length >= WRITE_BUFFER_PAUSE_THRESHOLD) {
|
|
||||||
// XOFF - stop pty pipe
|
|
||||||
// XON will be triggered by emulator before processing data chunk
|
|
||||||
this._coreService.triggerDataEvent(C0.DC3);
|
|
||||||
this._xoffSentToCatchUp = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._writeInProgress && this.writeBufferUtf8.length > 0) {
|
|
||||||
// Kick off a write which will write all data in sequence recursively
|
|
||||||
this._writeInProgress = true;
|
|
||||||
// Kick off an async innerWrite so more writes can come in while processing data
|
|
||||||
setTimeout(() => {
|
|
||||||
this._innerWriteUtf8();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected _innerWriteUtf8(bufferOffset: number = 0): void {
|
|
||||||
// Ensure the terminal isn't disposed
|
|
||||||
if (this._isDisposed) {
|
|
||||||
this.writeBufferUtf8 = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const startTime = Date.now();
|
|
||||||
while (this.writeBufferUtf8.length > bufferOffset) {
|
|
||||||
const data = this.writeBufferUtf8[bufferOffset];
|
|
||||||
bufferOffset++;
|
|
||||||
|
|
||||||
// If XOFF was sent in order to catch up with the pty process, resume it if
|
|
||||||
// we reached the end of the writeBuffer to allow more data to come in.
|
|
||||||
if (this._xoffSentToCatchUp && this.writeBufferUtf8.length === bufferOffset) {
|
|
||||||
this._coreService.triggerDataEvent(C0.DC1);
|
|
||||||
this._xoffSentToCatchUp = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._inputHandler.parseUtf8(data);
|
|
||||||
|
|
||||||
this.refresh(this._dirtyRowService.start, this._dirtyRowService.end);
|
|
||||||
|
|
||||||
if (Date.now() - startTime >= WRITE_TIMEOUT_MS) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.writeBufferUtf8.length > bufferOffset) {
|
|
||||||
// Allow renderer to catch up before processing the next batch
|
|
||||||
// trim already processed chunks if we are above threshold
|
|
||||||
if (bufferOffset > WRITE_BUFFER_LENGTH_THRESHOLD) {
|
|
||||||
this.writeBufferUtf8 = this.writeBufferUtf8.slice(bufferOffset);
|
|
||||||
bufferOffset = 0;
|
|
||||||
}
|
|
||||||
setTimeout(() => this._innerWriteUtf8(bufferOffset), 0);
|
|
||||||
} else {
|
|
||||||
this._writeInProgress = false;
|
|
||||||
this.writeBufferUtf8 = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes text to the terminal.
|
|
||||||
* @param data The text to write to the terminal.
|
|
||||||
*/
|
|
||||||
public write(data: string): void {
|
|
||||||
// Ensure the terminal isn't disposed
|
|
||||||
if (this._isDisposed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore falsy data values (including the empty string)
|
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.writeBuffer.push(data);
|
|
||||||
|
|
||||||
// Send XOFF to pause the pty process if the write buffer becomes too large so
|
|
||||||
// xterm.js can catch up before more data is sent. This is necessary in order
|
|
||||||
// to keep signals such as ^C responsive.
|
|
||||||
if (this.options.useFlowControl && !this._xoffSentToCatchUp && this.writeBuffer.length >= WRITE_BUFFER_PAUSE_THRESHOLD) {
|
|
||||||
// XOFF - stop pty pipe
|
|
||||||
// XON will be triggered by emulator before processing data chunk
|
|
||||||
this._coreService.triggerDataEvent(C0.DC3);
|
|
||||||
this._xoffSentToCatchUp = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._writeInProgress && this.writeBuffer.length > 0) {
|
|
||||||
// Kick off a write which will write all data in sequence recursively
|
|
||||||
this._writeInProgress = true;
|
|
||||||
// Kick off an async innerWrite so more writes can come in while processing data
|
|
||||||
setTimeout(() => {
|
|
||||||
this._innerWrite();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected _innerWrite(bufferOffset: number = 0): void {
|
|
||||||
// Ensure the terminal isn't disposed
|
|
||||||
if (this._isDisposed) {
|
|
||||||
this.writeBuffer = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const startTime = Date.now();
|
|
||||||
while (this.writeBuffer.length > bufferOffset) {
|
|
||||||
const data = this.writeBuffer[bufferOffset];
|
|
||||||
bufferOffset++;
|
|
||||||
|
|
||||||
// If XOFF was sent in order to catch up with the pty process, resume it if
|
|
||||||
// we reached the end of the writeBuffer to allow more data to come in.
|
|
||||||
if (this._xoffSentToCatchUp && this.writeBuffer.length === bufferOffset) {
|
|
||||||
this._coreService.triggerDataEvent(C0.DC1);
|
|
||||||
this._xoffSentToCatchUp = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._inputHandler.parse(data);
|
|
||||||
|
|
||||||
this.refresh(this._dirtyRowService.start, this._dirtyRowService.end);
|
|
||||||
|
|
||||||
if (Date.now() - startTime >= WRITE_TIMEOUT_MS) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.writeBuffer.length > bufferOffset) {
|
|
||||||
// Allow renderer to catch up before processing the next batch
|
|
||||||
// trim already processed chunks if we are above threshold
|
|
||||||
if (bufferOffset > WRITE_BUFFER_LENGTH_THRESHOLD) {
|
|
||||||
this.writeBuffer = this.writeBuffer.slice(bufferOffset);
|
|
||||||
bufferOffset = 0;
|
|
||||||
}
|
|
||||||
setTimeout(() => this._innerWrite(bufferOffset), 0);
|
|
||||||
} else {
|
|
||||||
this._writeInProgress = false;
|
|
||||||
this.writeBuffer = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes text to the terminal, followed by a break line character (\n).
|
|
||||||
* @param data The text to write to the terminal.
|
|
||||||
*/
|
|
||||||
public writeln(data: string): void {
|
|
||||||
this.write(data + '\r\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
public paste(data: string): void {
|
public paste(data: string): void {
|
||||||
paste(data, this.textarea, this.bracketedPasteMode, this._coreService);
|
paste(data, this.textarea, this.bracketedPasteMode, this._coreService);
|
||||||
}
|
}
|
||||||
@@ -1663,6 +1461,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
this._charSizeService.measure();
|
this._charSizeService.measure();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync the scroll area to make sure scroll events don't fire and scroll the viewport to an
|
||||||
|
// invalid location
|
||||||
|
this.viewport.syncScrollArea(true);
|
||||||
|
|
||||||
this.refresh(0, this.rows - 1);
|
this.refresh(0, this.rows - 1);
|
||||||
this._onResize.fire({ cols: x, rows: y });
|
this._onResize.fire({ cols: x, rows: y });
|
||||||
}
|
}
|
||||||
@@ -1743,10 +1545,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
const customKeyEventHandler = this._customKeyEventHandler;
|
const customKeyEventHandler = this._customKeyEventHandler;
|
||||||
const inputHandler = this._inputHandler;
|
const inputHandler = this._inputHandler;
|
||||||
const cursorState = this.cursorState;
|
const cursorState = this.cursorState;
|
||||||
const writeBuffer = this.writeBuffer;
|
|
||||||
const writeBufferUtf8 = this.writeBufferUtf8;
|
|
||||||
const writeInProgress = this._writeInProgress;
|
|
||||||
const xoffSentToCatchUp = this._xoffSentToCatchUp;
|
|
||||||
const userScrolling = this._userScrolling;
|
const userScrolling = this._userScrolling;
|
||||||
|
|
||||||
this._setup();
|
this._setup();
|
||||||
@@ -1761,10 +1559,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
this._customKeyEventHandler = customKeyEventHandler;
|
this._customKeyEventHandler = customKeyEventHandler;
|
||||||
this._inputHandler = inputHandler;
|
this._inputHandler = inputHandler;
|
||||||
this.cursorState = cursorState;
|
this.cursorState = cursorState;
|
||||||
this.writeBuffer = writeBuffer;
|
|
||||||
this.writeBufferUtf8 = writeBufferUtf8;
|
|
||||||
this._writeInProgress = writeInProgress;
|
|
||||||
this._xoffSentToCatchUp = xoffSentToCatchUp;
|
|
||||||
this._userScrolling = userScrolling;
|
this._userScrolling = userScrolling;
|
||||||
|
|
||||||
// do a full screen refresh
|
// do a full screen refresh
|
||||||
@@ -1795,6 +1589,14 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
|||||||
// return this.options.bellStyle === 'sound' ||
|
// return this.options.bellStyle === 'sound' ||
|
||||||
// this.options.bellStyle === 'both';
|
// this.options.bellStyle === 'both';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public write(data: string | Uint8Array, callback?: () => void): void {
|
||||||
|
this._writeBuffer.write(data, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeSync(data: string | Uint8Array): void {
|
||||||
|
this._writeBuffer.writeSync(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,10 +19,6 @@ import { IParams, IFunctionIdentifier } from 'common/parser/Types';
|
|||||||
import { ISelectionService } from 'browser/services/Services';
|
import { ISelectionService } from 'browser/services/Services';
|
||||||
|
|
||||||
export class TestTerminal extends Terminal {
|
export class TestTerminal extends Terminal {
|
||||||
writeSync(data: string): void {
|
|
||||||
this.writeBuffer.push(data);
|
|
||||||
this._innerWrite();
|
|
||||||
}
|
|
||||||
keyDown(ev: any): boolean { return this._keyDown(ev); }
|
keyDown(ev: any): boolean { return this._keyDown(ev); }
|
||||||
keyPress(ev: any): boolean { return this._keyPress(ev); }
|
keyPress(ev: any): boolean { return this._keyPress(ev); }
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+8
-8
@@ -73,8 +73,7 @@ export interface ICompositionHelper {
|
|||||||
* Calls the parser and handles actions generated by the parser.
|
* Calls the parser and handles actions generated by the parser.
|
||||||
*/
|
*/
|
||||||
export interface IInputHandler {
|
export interface IInputHandler {
|
||||||
parse(data: string): void;
|
parse(data: string | Uint8Array): void;
|
||||||
parseUtf8(data: Uint8Array): void;
|
|
||||||
print(data: Uint32Array, start: number, end: number): void;
|
print(data: Uint32Array, start: number, end: number): void;
|
||||||
|
|
||||||
/** C0 BEL */ bell(): void;
|
/** C0 BEL */ bell(): void;
|
||||||
@@ -86,7 +85,9 @@ export interface IInputHandler {
|
|||||||
/** C0 SI */ shiftIn(): void;
|
/** C0 SI */ shiftIn(): void;
|
||||||
|
|
||||||
/** CSI @ */ insertChars(params: IParams): void;
|
/** CSI @ */ insertChars(params: IParams): void;
|
||||||
|
/** CSI SP @ */ scrollLeft(params: IParams): void;
|
||||||
/** CSI A */ cursorUp(params: IParams): void;
|
/** CSI A */ cursorUp(params: IParams): void;
|
||||||
|
/** CSI SP A */ scrollRight(params: IParams): void;
|
||||||
/** CSI B */ cursorDown(params: IParams): void;
|
/** CSI B */ cursorDown(params: IParams): void;
|
||||||
/** CSI C */ cursorForward(params: IParams): void;
|
/** CSI C */ cursorForward(params: IParams): void;
|
||||||
/** CSI D */ cursorBackward(params: IParams): void;
|
/** CSI D */ cursorBackward(params: IParams): void;
|
||||||
@@ -122,6 +123,8 @@ export interface IInputHandler {
|
|||||||
/** CSI r */ setScrollRegion(params: IParams, collect?: string): void;
|
/** CSI r */ setScrollRegion(params: IParams, collect?: string): void;
|
||||||
/** CSI s */ saveCursor(params: IParams): void;
|
/** CSI s */ saveCursor(params: IParams): void;
|
||||||
/** CSI u */ restoreCursor(params: IParams): void;
|
/** CSI u */ restoreCursor(params: IParams): void;
|
||||||
|
/** CSI ' } */ insertColumns(params: IParams): void;
|
||||||
|
/** CSI ' ~ */ deleteColumns(params: IParams): void;
|
||||||
/** OSC 0
|
/** OSC 0
|
||||||
OSC 2 */ setTitle(data: string): void;
|
OSC 2 */ setTitle(data: string): void;
|
||||||
/** ESC E */ nextLine(): void;
|
/** ESC E */ nextLine(): void;
|
||||||
@@ -151,7 +154,6 @@ export interface IInputHandler {
|
|||||||
export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor {
|
export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor {
|
||||||
screenElement: HTMLElement;
|
screenElement: HTMLElement;
|
||||||
browser: IBrowser;
|
browser: IBrowser;
|
||||||
writeBuffer: string[];
|
|
||||||
cursorHidden: boolean;
|
cursorHidden: boolean;
|
||||||
cursorState: number;
|
cursorState: number;
|
||||||
buffer: IBuffer;
|
buffer: IBuffer;
|
||||||
@@ -175,7 +177,7 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc
|
|||||||
|
|
||||||
// Portions of the public API that are required by the internal Terminal
|
// Portions of the public API that are required by the internal Terminal
|
||||||
export interface IPublicTerminal extends IDisposable {
|
export interface IPublicTerminal extends IDisposable {
|
||||||
textarea: HTMLTextAreaElement;
|
textarea: HTMLTextAreaElement | undefined;
|
||||||
rows: number;
|
rows: number;
|
||||||
cols: number;
|
cols: number;
|
||||||
buffer: IBuffer;
|
buffer: IBuffer;
|
||||||
@@ -192,7 +194,6 @@ export interface IPublicTerminal extends IDisposable {
|
|||||||
blur(): void;
|
blur(): void;
|
||||||
focus(): void;
|
focus(): void;
|
||||||
resize(columns: number, rows: number): void;
|
resize(columns: number, rows: number): void;
|
||||||
writeln(data: string): void;
|
|
||||||
open(parent: HTMLElement): void;
|
open(parent: HTMLElement): void;
|
||||||
attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void;
|
attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void;
|
||||||
addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable;
|
addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable;
|
||||||
@@ -218,8 +219,7 @@ export interface IPublicTerminal extends IDisposable {
|
|||||||
scrollToBottom(): void;
|
scrollToBottom(): void;
|
||||||
scrollToLine(line: number): void;
|
scrollToLine(line: number): void;
|
||||||
clear(): void;
|
clear(): void;
|
||||||
write(data: string): void;
|
write(data: string | Uint8Array, callback?: () => void): void;
|
||||||
writeUtf8(data: Uint8Array): void;
|
|
||||||
paste(data: string): void;
|
paste(data: string): void;
|
||||||
refresh(start: number, end: number): void;
|
refresh(start: number, end: number): void;
|
||||||
reset(): void;
|
reset(): void;
|
||||||
@@ -230,7 +230,7 @@ export interface IBufferAccessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IElementAccessor {
|
export interface IElementAccessor {
|
||||||
readonly element: HTMLElement;
|
readonly element: HTMLElement | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILinkifierAccessor {
|
export interface ILinkifierAccessor {
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ export class Linkifier implements ILinkifier {
|
|||||||
*/
|
*/
|
||||||
private _doLinkifyRow(rowIndex: number, text: string, matcher: ILinkMatcher): void {
|
private _doLinkifyRow(rowIndex: number, text: string, matcher: ILinkMatcher): void {
|
||||||
// clone regex to do a global search on text
|
// clone regex to do a global search on text
|
||||||
const rex = new RegExp(matcher.regex.source, matcher.regex.flags + 'g');
|
const rex = new RegExp(matcher.regex.source, (matcher.regex.flags || '') + 'g');
|
||||||
let match;
|
let match;
|
||||||
let stringIndex = -1;
|
let stringIndex = -1;
|
||||||
while ((match = rex.exec(text)) !== null) {
|
while ((match = rex.exec(text)) !== null) {
|
||||||
@@ -306,7 +306,9 @@ export class Linkifier implements ILinkifier {
|
|||||||
e => {
|
e => {
|
||||||
this._onLinkTooltip.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg));
|
this._onLinkTooltip.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg));
|
||||||
if (matcher.hoverTooltipCallback) {
|
if (matcher.hoverTooltipCallback) {
|
||||||
matcher.hoverTooltipCallback(e, uri);
|
// Note that IViewportRange use 1-based coordinates to align with escape sequences such
|
||||||
|
// as CUP which use 1,1 as the default for row/col
|
||||||
|
matcher.hoverTooltipCallback(e, uri, { start: { x: x1, y: y1 }, end: { x: x2, y: y2 } });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
|
|||||||
Vendored
+14
-3
@@ -35,7 +35,7 @@ export interface IPartialColorSet {
|
|||||||
|
|
||||||
export interface IViewport extends IDisposable {
|
export interface IViewport extends IDisposable {
|
||||||
scrollBarWidth: number;
|
scrollBarWidth: number;
|
||||||
syncScrollArea(): void;
|
syncScrollArea(immediate?: boolean): void;
|
||||||
getLinesScrolled(ev: WheelEvent): number;
|
getLinesScrolled(ev: WheelEvent): number;
|
||||||
onWheel(ev: WheelEvent): boolean;
|
onWheel(ev: WheelEvent): boolean;
|
||||||
onTouchStart(ev: TouchEvent): void;
|
onTouchStart(ev: TouchEvent): void;
|
||||||
@@ -43,14 +43,25 @@ export interface IViewport extends IDisposable {
|
|||||||
onThemeChange(colors: IColorSet): void;
|
onThemeChange(colors: IColorSet): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IViewportRange {
|
||||||
|
start: IViewportRangePosition;
|
||||||
|
end: IViewportRangePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IViewportRangePosition {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
export type LinkMatcherHandler = (event: MouseEvent, uri: string) => void;
|
export type LinkMatcherHandler = (event: MouseEvent, uri: string) => void;
|
||||||
|
export type LinkMatcherHoverTooltipCallback = (event: MouseEvent, uri: string, position: IViewportRange) => void;
|
||||||
export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void;
|
export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void;
|
||||||
|
|
||||||
export interface ILinkMatcher {
|
export interface ILinkMatcher {
|
||||||
id: number;
|
id: number;
|
||||||
regex: RegExp;
|
regex: RegExp;
|
||||||
handler: LinkMatcherHandler;
|
handler: LinkMatcherHandler;
|
||||||
hoverTooltipCallback?: LinkMatcherHandler;
|
hoverTooltipCallback?: LinkMatcherHoverTooltipCallback;
|
||||||
hoverLeaveCallback?: () => void;
|
hoverLeaveCallback?: () => void;
|
||||||
matchIndex?: number;
|
matchIndex?: number;
|
||||||
validationCallback?: LinkMatcherValidationCallback;
|
validationCallback?: LinkMatcherValidationCallback;
|
||||||
@@ -96,7 +107,7 @@ export interface ILinkMatcherOptions {
|
|||||||
/**
|
/**
|
||||||
* A callback that fires when the mouse hovers over a link.
|
* A callback that fires when the mouse hovers over a link.
|
||||||
*/
|
*/
|
||||||
tooltipCallback?: LinkMatcherHandler;
|
tooltipCallback?: LinkMatcherHoverTooltipCallback;
|
||||||
/**
|
/**
|
||||||
* A callback that fires when the mouse leaves a link that was hovered.
|
* A callback that fires when the mouse leaves a link that was hovered.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+30
-11
@@ -7,7 +7,7 @@ import { Disposable } from 'common/Lifecycle';
|
|||||||
import { addDisposableDomListener } from 'browser/Lifecycle';
|
import { addDisposableDomListener } from 'browser/Lifecycle';
|
||||||
import { IColorSet, IViewport } from 'browser/Types';
|
import { IColorSet, IViewport } from 'browser/Types';
|
||||||
import { ICharSizeService, IRenderService } from 'browser/services/Services';
|
import { ICharSizeService, IRenderService } from 'browser/services/Services';
|
||||||
import { IBufferService } from 'common/services/Services';
|
import { IBufferService, IOptionsService } from 'common/services/Services';
|
||||||
|
|
||||||
const FALLBACK_SCROLL_BAR_WIDTH = 15;
|
const FALLBACK_SCROLL_BAR_WIDTH = 15;
|
||||||
|
|
||||||
@@ -37,6 +37,7 @@ export class Viewport extends Disposable implements IViewport {
|
|||||||
private readonly _viewportElement: HTMLElement,
|
private readonly _viewportElement: HTMLElement,
|
||||||
private readonly _scrollArea: HTMLElement,
|
private readonly _scrollArea: HTMLElement,
|
||||||
@IBufferService private readonly _bufferService: IBufferService,
|
@IBufferService private readonly _bufferService: IBufferService,
|
||||||
|
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||||
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
||||||
@IRenderService private readonly _renderService: IRenderService
|
@IRenderService private readonly _renderService: IRenderService
|
||||||
) {
|
) {
|
||||||
@@ -60,7 +61,14 @@ export class Viewport extends Disposable implements IViewport {
|
|||||||
* Refreshes row height, setting line-height, viewport height and scroll area height if
|
* Refreshes row height, setting line-height, viewport height and scroll area height if
|
||||||
* necessary.
|
* necessary.
|
||||||
*/
|
*/
|
||||||
private _refresh(): void {
|
private _refresh(immediate: boolean): void {
|
||||||
|
if (immediate) {
|
||||||
|
this._innerRefresh();
|
||||||
|
if (this._refreshAnimationFrame !== null) {
|
||||||
|
cancelAnimationFrame(this._refreshAnimationFrame);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this._refreshAnimationFrame === null) {
|
if (this._refreshAnimationFrame === null) {
|
||||||
this._refreshAnimationFrame = requestAnimationFrame(() => this._innerRefresh());
|
this._refreshAnimationFrame = requestAnimationFrame(() => this._innerRefresh());
|
||||||
}
|
}
|
||||||
@@ -88,40 +96,39 @@ export class Viewport extends Disposable implements IViewport {
|
|||||||
|
|
||||||
this._refreshAnimationFrame = null;
|
this._refreshAnimationFrame = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates dimensions and synchronizes the scroll area if necessary.
|
* Updates dimensions and synchronizes the scroll area if necessary.
|
||||||
*/
|
*/
|
||||||
public syncScrollArea(): void {
|
public syncScrollArea(immediate: boolean = false): void {
|
||||||
// If buffer height changed
|
// If buffer height changed
|
||||||
if (this._lastRecordedBufferLength !== this._bufferService.buffer.lines.length) {
|
if (this._lastRecordedBufferLength !== this._bufferService.buffer.lines.length) {
|
||||||
this._lastRecordedBufferLength = this._bufferService.buffer.lines.length;
|
this._lastRecordedBufferLength = this._bufferService.buffer.lines.length;
|
||||||
this._refresh();
|
this._refresh(immediate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If viewport height changed
|
// If viewport height changed
|
||||||
if (this._lastRecordedViewportHeight !== this._renderService.dimensions.canvasHeight) {
|
if (this._lastRecordedViewportHeight !== this._renderService.dimensions.canvasHeight) {
|
||||||
this._refresh();
|
this._refresh(immediate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the buffer position doesn't match last scroll top
|
// If the buffer position doesn't match last scroll top
|
||||||
const newScrollTop = this._bufferService.buffer.ydisp * this._currentRowHeight;
|
const newScrollTop = this._bufferService.buffer.ydisp * this._currentRowHeight;
|
||||||
if (this._lastScrollTop !== newScrollTop) {
|
if (this._lastScrollTop !== newScrollTop) {
|
||||||
this._refresh();
|
this._refresh(immediate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If element's scroll top changed, this can happen when hiding the element
|
// If element's scroll top changed, this can happen when hiding the element
|
||||||
if (this._lastScrollTop !== this._viewportElement.scrollTop) {
|
if (this._lastScrollTop !== this._viewportElement.scrollTop) {
|
||||||
this._refresh();
|
this._refresh(immediate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If row height changed
|
// If row height changed
|
||||||
if (this._renderService.dimensions.scaledCellHeight / window.devicePixelRatio !== this._currentRowHeight) {
|
if (this._renderService.dimensions.scaledCellHeight / window.devicePixelRatio !== this._currentRowHeight) {
|
||||||
this._refresh();
|
this._refresh(immediate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,7 +198,7 @@ export class Viewport extends Disposable implements IViewport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to WheelEvent.DOM_DELTA_PIXEL
|
// Fallback to WheelEvent.DOM_DELTA_PIXEL
|
||||||
let amount = ev.deltaY;
|
let amount = this._applyScrollModifier(ev.deltaY, ev);
|
||||||
if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
|
if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
|
||||||
amount *= this._currentRowHeight;
|
amount *= this._currentRowHeight;
|
||||||
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
|
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
|
||||||
@@ -212,7 +219,7 @@ export class Viewport extends Disposable implements IViewport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to WheelEvent.DOM_DELTA_LINE
|
// Fallback to WheelEvent.DOM_DELTA_LINE
|
||||||
let amount = ev.deltaY;
|
let amount = this._applyScrollModifier(ev.deltaY, ev);
|
||||||
if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
|
if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
|
||||||
amount /= this._currentRowHeight + 0.0; // Prevent integer division
|
amount /= this._currentRowHeight + 0.0; // Prevent integer division
|
||||||
this._wheelPartialScroll += amount;
|
this._wheelPartialScroll += amount;
|
||||||
@@ -224,6 +231,18 @@ export class Viewport extends Disposable implements IViewport {
|
|||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _applyScrollModifier(amount: number, ev: WheelEvent): number {
|
||||||
|
const modifier = this._optionsService.options.fastScrollModifier;
|
||||||
|
// Multiply the scroll speed when the modifier is down
|
||||||
|
if ((modifier === 'alt' && ev.altKey) ||
|
||||||
|
(modifier === 'ctrl' && ev.ctrlKey) ||
|
||||||
|
(modifier === 'shift' && ev.shiftKey)) {
|
||||||
|
return amount * this._optionsService.options.fastScrollSensitivity * this._optionsService.options.scrollSensitivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return amount * this._optionsService.options.scrollSensitivity;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the touchstart event, recording the touch occurred.
|
* Handles the touchstart event, recording the touch occurred.
|
||||||
* @param ev The touch event.
|
* @param ev The touch event.
|
||||||
|
|||||||
@@ -303,7 +303,6 @@ export class CharacterJoinerRegistry implements ICharacterJoinerRegistry {
|
|||||||
// current range
|
// current range
|
||||||
ranges[i - 1][1] = Math.max(newRange[1], range[1]);
|
ranges[i - 1][1] = Math.max(newRange[1], range[1]);
|
||||||
ranges.splice(i, 1);
|
ranges.splice(i, 1);
|
||||||
inRange = false;
|
|
||||||
return ranges;
|
return ranges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
|
|||||||
import { AttributeData } from 'common/buffer/AttributeData';
|
import { AttributeData } from 'common/buffer/AttributeData';
|
||||||
import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants';
|
import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants';
|
||||||
import { CellData } from 'common/buffer/CellData';
|
import { CellData } from 'common/buffer/CellData';
|
||||||
import { ITerminalOptions, IOptionsService } from 'common/services/Services';
|
import { IOptionsService } from 'common/services/Services';
|
||||||
|
|
||||||
export const BOLD_CLASS = 'xterm-bold';
|
export const BOLD_CLASS = 'xterm-bold';
|
||||||
export const DIM_CLASS = 'xterm-dim';
|
export const DIM_CLASS = 'xterm-dim';
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ export class SelectionService implements ISelectionService {
|
|||||||
for (let i = start[1] + 1; i <= end[1] - 1; i++) {
|
for (let i = start[1] + 1; i <= end[1] - 1; i++) {
|
||||||
const bufferLine = buffer.lines.get(i);
|
const bufferLine = buffer.lines.get(i);
|
||||||
const lineText = buffer.translateBufferLineToString(i, true);
|
const lineText = buffer.translateBufferLineToString(i, true);
|
||||||
if (bufferLine!.isWrapped) {
|
if (bufferLine && bufferLine.isWrapped) {
|
||||||
result[result.length - 1] += lineText;
|
result[result.length - 1] += lineText;
|
||||||
} else {
|
} else {
|
||||||
result.push(lineText);
|
result.push(lineText);
|
||||||
@@ -221,7 +221,7 @@ export class SelectionService implements ISelectionService {
|
|||||||
if (start[1] !== end[1]) {
|
if (start[1] !== end[1]) {
|
||||||
const bufferLine = buffer.lines.get(end[1]);
|
const bufferLine = buffer.lines.get(end[1]);
|
||||||
const lineText = buffer.translateBufferLineToString(end[1], true, 0, end[0]);
|
const lineText = buffer.translateBufferLineToString(end[1], true, 0, end[0]);
|
||||||
if (bufferLine!.isWrapped) {
|
if (bufferLine && bufferLine!.isWrapped) {
|
||||||
result[result.length - 1] += lineText;
|
result[result.length - 1] += lineText;
|
||||||
} else {
|
} else {
|
||||||
result.push(lineText);
|
result.push(lineText);
|
||||||
|
|||||||
+18
-12
@@ -167,19 +167,25 @@ export class Buffer implements IBuffer {
|
|||||||
if (this._rows < newRows) {
|
if (this._rows < newRows) {
|
||||||
for (let y = this._rows; y < newRows; y++) {
|
for (let y = this._rows; y < newRows; y++) {
|
||||||
if (this.lines.length < newRows + this.ybase) {
|
if (this.lines.length < newRows + this.ybase) {
|
||||||
if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) {
|
if (this._optionsService.options.windowsMode) {
|
||||||
// There is room above the buffer and there are no empty elements below the line,
|
// Just add the new missing rows on Windows as conpty reprints the screen with it's
|
||||||
// scroll up
|
// view of the world. Once a line enters scrollback for conpty it remains there
|
||||||
this.ybase--;
|
|
||||||
addToY++;
|
|
||||||
if (this.ydisp > 0) {
|
|
||||||
// Viewport is at the top of the buffer, must increase downwards
|
|
||||||
this.ydisp--;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Add a blank line if there is no buffer left at the top to scroll to, or if there
|
|
||||||
// are blank lines after the cursor
|
|
||||||
this.lines.push(new BufferLine(newCols, nullCell));
|
this.lines.push(new BufferLine(newCols, nullCell));
|
||||||
|
} else {
|
||||||
|
if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) {
|
||||||
|
// There is room above the buffer and there are no empty elements below the line,
|
||||||
|
// scroll up
|
||||||
|
this.ybase--;
|
||||||
|
addToY++;
|
||||||
|
if (this.ydisp > 0) {
|
||||||
|
// Viewport is at the top of the buffer, must increase downwards
|
||||||
|
this.ydisp--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Add a blank line if there is no buffer left at the top to scroll to, or if there
|
||||||
|
// are blank lines after the cursor
|
||||||
|
this.lines.push(new BufferLine(newCols, nullCell));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { CharData, IBufferLine, ICellData } from 'common/Types';
|
import { CharData, IBufferLine, ICellData } from 'common/Types';
|
||||||
import { stringFromCodePoint } from 'common/input/TextDecoder';
|
import { stringFromCodePoint } from 'common/input/TextDecoder';
|
||||||
import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants';
|
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants';
|
||||||
import { CellData } from 'common/buffer/CellData';
|
import { CellData } from 'common/buffer/CellData';
|
||||||
import { AttributeData } from 'common/buffer/AttributeData';
|
import { AttributeData } from 'common/buffer/AttributeData';
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export class Marker extends Disposable implements IMarker {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.isDisposed = true;
|
this.isDisposed = true;
|
||||||
|
this.line = -1;
|
||||||
// Emit before super.dispose such that dispose listeners get a change to react
|
// Emit before super.dispose such that dispose listeners get a change to react
|
||||||
this._onDispose.fire();
|
this._onDispose.fire();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,12 @@ describe('Keyboard', () => {
|
|||||||
it('should return \\x1b[5C for alt+right', () => {
|
it('should return \\x1b[5C for alt+right', () => {
|
||||||
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: false }).key, '\x1b[1;5C'); // CSI 5 C
|
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: false }).key, '\x1b[1;5C'); // CSI 5 C
|
||||||
});
|
});
|
||||||
|
it('should return \\x1b[5D for alt+up', () => {
|
||||||
|
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: false }).key, '\x1b[1;5A'); // CSI 5 D
|
||||||
|
});
|
||||||
|
it('should return \\x1b[5C for alt+down', () => {
|
||||||
|
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: false }).key, '\x1b[1;5B'); // CSI 5 C
|
||||||
|
});
|
||||||
it('should return \\x1ba for alt+a', () => {
|
it('should return \\x1ba for alt+a', () => {
|
||||||
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: false }).key, '\x1ba');
|
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: false }).key, '\x1ba');
|
||||||
});
|
});
|
||||||
@@ -120,6 +126,12 @@ describe('Keyboard', () => {
|
|||||||
it('should return \\x1bf for alt+right', () => {
|
it('should return \\x1bf for alt+right', () => {
|
||||||
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: true }).key, '\x1bf'); // CSI 5 C
|
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: true }).key, '\x1bf'); // CSI 5 C
|
||||||
});
|
});
|
||||||
|
it('should return \\x1bb for alt+up', () => {
|
||||||
|
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: true }).key, '\x1b[1;3A'); // CSI 5 D
|
||||||
|
});
|
||||||
|
it('should return \\x1bf for alt+down', () => {
|
||||||
|
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: true }).key, '\x1b[1;3B'); // CSI 5 C
|
||||||
|
});
|
||||||
it('should return undefined for alt+a', () => {
|
it('should return undefined for alt+a', () => {
|
||||||
assert.strictEqual(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: true }).key, undefined), { isMac: true };
|
assert.strictEqual(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: true }).key, undefined), { isMac: true };
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -113,13 +113,16 @@ export function evaluateKeyboardEvent(
|
|||||||
break;
|
break;
|
||||||
case 37:
|
case 37:
|
||||||
// left-arrow
|
// left-arrow
|
||||||
|
if (ev.metaKey) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (modifiers) {
|
if (modifiers) {
|
||||||
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'D';
|
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'D';
|
||||||
// HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards
|
// HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards
|
||||||
// http://unix.stackexchange.com/a/108106
|
// http://unix.stackexchange.com/a/108106
|
||||||
// macOS uses different escape sequences than linux
|
// macOS uses different escape sequences than linux
|
||||||
if (result.key === C0.ESC + '[1;3D') {
|
if (result.key === C0.ESC + '[1;3D') {
|
||||||
result.key = isMac ? C0.ESC + 'b' : C0.ESC + '[1;5D';
|
result.key = C0.ESC + (isMac ? 'b' : '[1;5D');
|
||||||
}
|
}
|
||||||
} else if (applicationCursorMode) {
|
} else if (applicationCursorMode) {
|
||||||
result.key = C0.ESC + 'OD';
|
result.key = C0.ESC + 'OD';
|
||||||
@@ -129,13 +132,16 @@ export function evaluateKeyboardEvent(
|
|||||||
break;
|
break;
|
||||||
case 39:
|
case 39:
|
||||||
// right-arrow
|
// right-arrow
|
||||||
|
if (ev.metaKey) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (modifiers) {
|
if (modifiers) {
|
||||||
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'C';
|
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'C';
|
||||||
// HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward
|
// HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward
|
||||||
// http://unix.stackexchange.com/a/108106
|
// http://unix.stackexchange.com/a/108106
|
||||||
// macOS uses different escape sequences than linux
|
// macOS uses different escape sequences than linux
|
||||||
if (result.key === C0.ESC + '[1;3C') {
|
if (result.key === C0.ESC + '[1;3C') {
|
||||||
result.key = isMac ? C0.ESC + 'f' : C0.ESC + '[1;5C';
|
result.key = C0.ESC + (isMac ? 'f' : '[1;5C');
|
||||||
}
|
}
|
||||||
} else if (applicationCursorMode) {
|
} else if (applicationCursorMode) {
|
||||||
result.key = C0.ESC + 'OC';
|
result.key = C0.ESC + 'OC';
|
||||||
@@ -145,11 +151,15 @@ export function evaluateKeyboardEvent(
|
|||||||
break;
|
break;
|
||||||
case 38:
|
case 38:
|
||||||
// up-arrow
|
// up-arrow
|
||||||
|
if (ev.metaKey) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (modifiers) {
|
if (modifiers) {
|
||||||
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'A';
|
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'A';
|
||||||
// HACK: Make Alt + up-arrow behave like Ctrl + up-arrow
|
// HACK: Make Alt + up-arrow behave like Ctrl + up-arrow
|
||||||
// http://unix.stackexchange.com/a/108106
|
// http://unix.stackexchange.com/a/108106
|
||||||
if (result.key === C0.ESC + '[1;3A') {
|
// macOS uses different escape sequences than linux
|
||||||
|
if (!isMac && result.key === C0.ESC + '[1;3A') {
|
||||||
result.key = C0.ESC + '[1;5A';
|
result.key = C0.ESC + '[1;5A';
|
||||||
}
|
}
|
||||||
} else if (applicationCursorMode) {
|
} else if (applicationCursorMode) {
|
||||||
@@ -160,11 +170,15 @@ export function evaluateKeyboardEvent(
|
|||||||
break;
|
break;
|
||||||
case 40:
|
case 40:
|
||||||
// down-arrow
|
// down-arrow
|
||||||
|
if (ev.metaKey) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (modifiers) {
|
if (modifiers) {
|
||||||
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'B';
|
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'B';
|
||||||
// HACK: Make Alt + down-arrow behave like Ctrl + down-arrow
|
// HACK: Make Alt + down-arrow behave like Ctrl + down-arrow
|
||||||
// http://unix.stackexchange.com/a/108106
|
// http://unix.stackexchange.com/a/108106
|
||||||
if (result.key === C0.ESC + '[1;3B') {
|
// macOS uses different escape sequences than linux
|
||||||
|
if (!isMac && result.key === C0.ESC + '[1;3B') {
|
||||||
result.key = C0.ESC + '[1;5B';
|
result.key = C0.ESC + '[1;5B';
|
||||||
}
|
}
|
||||||
} else if (applicationCursorMode) {
|
} else if (applicationCursorMode) {
|
||||||
@@ -316,23 +330,18 @@ export function evaluateKeyboardEvent(
|
|||||||
if (ev.keyCode >= 65 && ev.keyCode <= 90) {
|
if (ev.keyCode >= 65 && ev.keyCode <= 90) {
|
||||||
result.key = String.fromCharCode(ev.keyCode - 64);
|
result.key = String.fromCharCode(ev.keyCode - 64);
|
||||||
} else if (ev.keyCode === 32) {
|
} else if (ev.keyCode === 32) {
|
||||||
// NUL
|
result.key = C0.NUL;
|
||||||
result.key = String.fromCharCode(0);
|
|
||||||
} else if (ev.keyCode >= 51 && ev.keyCode <= 55) {
|
} else if (ev.keyCode >= 51 && ev.keyCode <= 55) {
|
||||||
// escape, file sep, group sep, record sep, unit sep
|
// escape, file sep, group sep, record sep, unit sep
|
||||||
result.key = String.fromCharCode(ev.keyCode - 51 + 27);
|
result.key = String.fromCharCode(ev.keyCode - 51 + 27);
|
||||||
} else if (ev.keyCode === 56) {
|
} else if (ev.keyCode === 56) {
|
||||||
// delete
|
result.key = C0.DEL;
|
||||||
result.key = String.fromCharCode(127);
|
|
||||||
} else if (ev.keyCode === 219) {
|
} else if (ev.keyCode === 219) {
|
||||||
// ^[ - Control Sequence Introducer (CSI)
|
result.key = C0.ESC;
|
||||||
result.key = String.fromCharCode(27);
|
|
||||||
} else if (ev.keyCode === 220) {
|
} else if (ev.keyCode === 220) {
|
||||||
// ^\ - String Terminator (ST)
|
result.key = C0.FS;
|
||||||
result.key = String.fromCharCode(28);
|
|
||||||
} else if (ev.keyCode === 221) {
|
} else if (ev.keyCode === 221) {
|
||||||
// ^] - Operating System Command (OSC)
|
result.key = C0.GS;
|
||||||
result.key = String.fromCharCode(29);
|
|
||||||
}
|
}
|
||||||
} else if ((!isMac || macOptionIsMeta) && ev.altKey && !ev.metaKey) {
|
} else if ((!isMac || macOptionIsMeta) && ev.altKey && !ev.metaKey) {
|
||||||
// On macOS this is a third level shift when !macOptionIsMeta. Use <Esc> instead.
|
// On macOS this is a third level shift when !macOptionIsMeta. Use <Esc> instead.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { assert } from 'chai';
|
|||||||
import { StringToUtf32, stringFromCodePoint, Utf8ToUtf32, utf32ToString } from 'common/input/TextDecoder';
|
import { StringToUtf32, stringFromCodePoint, Utf8ToUtf32, utf32ToString } from 'common/input/TextDecoder';
|
||||||
import { encode } from 'utf8';
|
import { encode } from 'utf8';
|
||||||
|
|
||||||
|
|
||||||
// convert UTF32 codepoints to string
|
// convert UTF32 codepoints to string
|
||||||
function toString(data: Uint32Array, length: number): string {
|
function toString(data: Uint32Array, length: number): string {
|
||||||
if ((String as any).fromCodePoint) {
|
if ((String as any).fromCodePoint) {
|
||||||
@@ -214,6 +215,16 @@ describe('text encodings', () => {
|
|||||||
}
|
}
|
||||||
assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
|
assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
|
||||||
});
|
});
|
||||||
|
it('test break after 3 bytes - issue #2495', () => {
|
||||||
|
const decoder = new Utf8ToUtf32();
|
||||||
|
const target = new Uint32Array(5);
|
||||||
|
const utf8Data = fromByteString('\xf0\xa0\x9c\x8e');
|
||||||
|
let written = decoder.decode(utf8Data.slice(0, 3), target);
|
||||||
|
assert.equal(written, 0);
|
||||||
|
written = decoder.decode(utf8Data.slice(3), target);
|
||||||
|
assert.equal(written, 1);
|
||||||
|
assert(toString(target, written), '𠜎');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ export class Utf8ToUtf32 {
|
|||||||
target[size++] = cp;
|
target[size++] = cp;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (codepoint < 0x010000 || codepoint > 0x10FFFF) {
|
if (cp < 0x010000 || cp > 0x10FFFF) {
|
||||||
// illegal codepoint
|
// illegal codepoint
|
||||||
} else {
|
} else {
|
||||||
target[size++] = cp;
|
target[size++] = cp;
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { assert } from 'chai';
|
||||||
|
import { WriteBuffer } from './WriteBuffer';
|
||||||
|
|
||||||
|
declare let Buffer: any;
|
||||||
|
|
||||||
|
function toBytes(s: string): Uint8Array {
|
||||||
|
return Buffer.from(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromBytes(bytes: Uint8Array): string {
|
||||||
|
return bytes.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('WriteBuffer', () => {
|
||||||
|
let wb: WriteBuffer;
|
||||||
|
let stack: (string | Uint8Array)[] = [];
|
||||||
|
let cbStack: string[] = [];
|
||||||
|
beforeEach(() => {
|
||||||
|
stack = [];
|
||||||
|
cbStack = [];
|
||||||
|
wb = new WriteBuffer(data => { stack.push(data); });
|
||||||
|
});
|
||||||
|
describe('write input', () => {
|
||||||
|
it('string', done => {
|
||||||
|
wb.write('a._');
|
||||||
|
wb.write('b.x', () => { cbStack.push('b'); });
|
||||||
|
wb.write('c._');
|
||||||
|
wb.write('d.x', () => { cbStack.push('d'); });
|
||||||
|
wb.write('e', () => {
|
||||||
|
assert.deepEqual(stack, ['a._', 'b.x', 'c._', 'd.x', 'e']);
|
||||||
|
assert.deepEqual(cbStack, ['b', 'd']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('bytes', done => {
|
||||||
|
wb.write(toBytes('a._'));
|
||||||
|
wb.write(toBytes('b.x'), () => { cbStack.push('b'); });
|
||||||
|
wb.write(toBytes('c._'));
|
||||||
|
wb.write(toBytes('d.x'), () => { cbStack.push('d'); });
|
||||||
|
wb.write(toBytes('e'), () => {
|
||||||
|
assert.deepEqual(stack.map(val => typeof val === 'string' ? '' : fromBytes(val)), ['a._', 'b.x', 'c._', 'd.x', 'e']);
|
||||||
|
assert.deepEqual(cbStack, ['b', 'd']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('string/bytes mixed', done => {
|
||||||
|
wb.write('a._');
|
||||||
|
wb.write('b.x', () => { cbStack.push('b'); });
|
||||||
|
wb.write(toBytes('c._'));
|
||||||
|
wb.write(toBytes('d.x'), () => { cbStack.push('d'); });
|
||||||
|
wb.write(toBytes('e'), () => {
|
||||||
|
assert.deepEqual(stack.map(val => typeof val === 'string' ? val : fromBytes(val)), ['a._', 'b.x', 'c._', 'd.x', 'e']);
|
||||||
|
assert.deepEqual(cbStack, ['b', 'd']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('write callback works for empty chunks', done => {
|
||||||
|
wb.write('a', () => { cbStack.push('a'); });
|
||||||
|
wb.write('', () => { cbStack.push('b'); });
|
||||||
|
wb.write(toBytes('c'), () => { cbStack.push('c'); });
|
||||||
|
wb.write(new Uint8Array(0), () => { cbStack.push('d'); });
|
||||||
|
wb.write('e', () => {
|
||||||
|
assert.deepEqual(stack.map(val => typeof val === 'string' ? val : fromBytes(val)), ['a', '', 'c', '', 'e']);
|
||||||
|
assert.deepEqual(cbStack, ['a', 'b', 'c', 'd']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('writeSync', done => {
|
||||||
|
wb.write('a', () => { cbStack.push('a'); });
|
||||||
|
wb.write('b', () => { cbStack.push('b'); });
|
||||||
|
wb.write('c', () => { cbStack.push('c'); });
|
||||||
|
wb.writeSync('d');
|
||||||
|
assert.deepEqual(stack, ['a', 'b', 'c', 'd']);
|
||||||
|
assert.deepEqual(cbStack, ['a', 'b', 'c']);
|
||||||
|
wb.write('x', () => { cbStack.push('x'); });
|
||||||
|
wb.write('', () => {
|
||||||
|
assert.deepEqual(stack, ['a', 'b', 'c', 'd', 'x', '']);
|
||||||
|
assert.deepEqual(cbStack, ['a', 'b', 'c', 'x']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare const setTimeout: (handler: () => void, timeout?: number) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safety watermark to avoid memory exhaustion and browser engine crash on fast data input.
|
||||||
|
* Enable flow control to avoid this limit and make sure that your backend correctly
|
||||||
|
* propagates this to the underlying pty. (see docs for further instructions)
|
||||||
|
* Since this limit is meant as a safety parachute to prevent browser crashs,
|
||||||
|
* it is set to a very high number. Typically xterm.js gets unresponsive with
|
||||||
|
* a 100 times lower number (>500 kB).
|
||||||
|
*/
|
||||||
|
const DISCARD_WATERMARK = 50000000; // ~50 MB
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The max number of ms to spend on writes before allowing the renderer to
|
||||||
|
* catch up with a 0ms setTimeout. A value of < 33 to keep us close to
|
||||||
|
* 30fps, and a value of < 16 to try to run at 60fps. Of course, the real FPS
|
||||||
|
* depends on the time it takes for the renderer to draw the frame.
|
||||||
|
*/
|
||||||
|
const WRITE_TIMEOUT_MS = 12;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Threshold of max held chunks in the write buffer, that were already processed.
|
||||||
|
* This is a tradeoff between extensive write buffer shifts (bad runtime) and high
|
||||||
|
* memory consumption by data thats not used anymore.
|
||||||
|
*/
|
||||||
|
const WRITE_BUFFER_LENGTH_THRESHOLD = 50;
|
||||||
|
|
||||||
|
export class WriteBuffer {
|
||||||
|
private _writeBuffer: (string | Uint8Array)[] = [];
|
||||||
|
private _callbacks: ((() => void) | undefined)[] = [];
|
||||||
|
private _pendingData = 0;
|
||||||
|
private _bufferOffset = 0;
|
||||||
|
|
||||||
|
constructor(private _action: (data: string | Uint8Array) => void) { }
|
||||||
|
|
||||||
|
public writeSync(data: string | Uint8Array): void {
|
||||||
|
// force sync processing on pending data chunks to avoid in-band data scrambling
|
||||||
|
// does the same as innerWrite but without event loop
|
||||||
|
if (this._writeBuffer.length) {
|
||||||
|
for (let i = this._bufferOffset; i < this._writeBuffer.length; ++i) {
|
||||||
|
const data = this._writeBuffer[i];
|
||||||
|
const cb = this._callbacks[i];
|
||||||
|
this._action(data);
|
||||||
|
if (cb) cb();
|
||||||
|
}
|
||||||
|
// reset all to avoid reprocessing of chunks with scheduled innerWrite call
|
||||||
|
this._writeBuffer = [];
|
||||||
|
this._callbacks = [];
|
||||||
|
this._pendingData = 0;
|
||||||
|
// stop scheduled innerWrite by offset > length condition
|
||||||
|
this._bufferOffset = 0x7FFFFFFF;
|
||||||
|
}
|
||||||
|
// handle current data chunk
|
||||||
|
this._action(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public write(data: string | Uint8Array, callback?: () => void): void {
|
||||||
|
if (this._pendingData > DISCARD_WATERMARK) {
|
||||||
|
throw new Error('write data discarded, use flow control to avoid losing data');
|
||||||
|
}
|
||||||
|
|
||||||
|
// schedule chunk processing for next event loop run
|
||||||
|
if (!this._writeBuffer.length) {
|
||||||
|
this._bufferOffset = 0;
|
||||||
|
setTimeout(() => this._innerWrite());
|
||||||
|
}
|
||||||
|
|
||||||
|
this._pendingData += data.length;
|
||||||
|
this._writeBuffer.push(data);
|
||||||
|
this._callbacks.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _innerWrite(): void {
|
||||||
|
const startTime = Date.now();
|
||||||
|
while (this._writeBuffer.length > this._bufferOffset) {
|
||||||
|
const data = this._writeBuffer[this._bufferOffset];
|
||||||
|
const cb = this._callbacks[this._bufferOffset];
|
||||||
|
this._bufferOffset++;
|
||||||
|
|
||||||
|
this._action(data);
|
||||||
|
this._pendingData -= data.length;
|
||||||
|
if (cb) cb();
|
||||||
|
|
||||||
|
if (Date.now() - startTime >= WRITE_TIMEOUT_MS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this._writeBuffer.length > this._bufferOffset) {
|
||||||
|
// Allow renderer to catch up before processing the next batch
|
||||||
|
// trim already processed chunks if we are above threshold
|
||||||
|
if (this._bufferOffset > WRITE_BUFFER_LENGTH_THRESHOLD) {
|
||||||
|
this._writeBuffer = this._writeBuffer.slice(this._bufferOffset);
|
||||||
|
this._callbacks = this._callbacks.slice(this._bufferOffset);
|
||||||
|
this._bufferOffset = 0;
|
||||||
|
}
|
||||||
|
setTimeout(() => this._innerWrite(), 0);
|
||||||
|
} else {
|
||||||
|
this._writeBuffer = [];
|
||||||
|
this._callbacks = [];
|
||||||
|
this._pendingData = 0;
|
||||||
|
this._bufferOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,9 +32,9 @@ describe('CoreMouseService', () => {
|
|||||||
const cms = new CoreMouseService(bufferService, coreService);
|
const cms = new CoreMouseService(bufferService, coreService);
|
||||||
assert.deepEqual(Object.keys((cms as any)._protocols), ['NONE', 'X10', 'VT200', 'DRAG', 'ANY']);
|
assert.deepEqual(Object.keys((cms as any)._protocols), ['NONE', 'X10', 'VT200', 'DRAG', 'ANY']);
|
||||||
});
|
});
|
||||||
it('default encodings - DEFAULT, UTF8, SGR, URXVT', () => {
|
it('default encodings - DEFAULT, SGR', () => {
|
||||||
const cms = new CoreMouseService(bufferService, coreService);
|
const cms = new CoreMouseService(bufferService, coreService);
|
||||||
assert.deepEqual(Object.keys((cms as any)._encodings), ['DEFAULT', 'UTF8', 'SGR', 'URXVT']);
|
assert.deepEqual(Object.keys((cms as any)._encodings), ['DEFAULT', 'SGR']);
|
||||||
});
|
});
|
||||||
it('protocol/encoding setter, reset', () => {
|
it('protocol/encoding setter, reset', () => {
|
||||||
const cms = new CoreMouseService(bufferService, coreService);
|
const cms = new CoreMouseService(bufferService, coreService);
|
||||||
@@ -151,14 +151,6 @@ describe('CoreMouseService', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
it('UTF8 encoding', () => {
|
|
||||||
cms.activeProtocol = 'ANY';
|
|
||||||
cms.activeEncoding = 'UTF8';
|
|
||||||
for (let i = 0; i < bufferService.cols; ++i) {
|
|
||||||
assert.equal(cms.triggerMouseEvent({ col: i, row: 0, button: CoreMouseButton.LEFT, action: CoreMouseAction.DOWN }), true);
|
|
||||||
assert.deepEqual(toBytes(reports.pop()), [0x1b, 0x5b, 0x4d, 0x20, i + 33, 0x21]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
it('SGR encoding', () => {
|
it('SGR encoding', () => {
|
||||||
cms.activeProtocol = 'ANY';
|
cms.activeProtocol = 'ANY';
|
||||||
cms.activeEncoding = 'SGR';
|
cms.activeEncoding = 'SGR';
|
||||||
@@ -167,14 +159,6 @@ describe('CoreMouseService', () => {
|
|||||||
assert.deepEqual(reports.pop(), `\x1b[<0;${i + 1};1M`);
|
assert.deepEqual(reports.pop(), `\x1b[<0;${i + 1};1M`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
it('URXVT', () => {
|
|
||||||
cms.activeProtocol = 'ANY';
|
|
||||||
cms.activeEncoding = 'URXVT';
|
|
||||||
for (let i = 0; i < bufferService.cols; ++i) {
|
|
||||||
assert.equal(cms.triggerMouseEvent({ col: i, row: 0, button: CoreMouseButton.LEFT, action: CoreMouseAction.DOWN }), true);
|
|
||||||
assert.deepEqual(reports.pop(), `\x1b[32;${i + 1};1M`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
it('eventCodes with modifiers (DEFAULT encoding)', () => {
|
it('eventCodes with modifiers (DEFAULT encoding)', () => {
|
||||||
// TODO: implement AUX button tests
|
// TODO: implement AUX button tests
|
||||||
|
|||||||
@@ -132,17 +132,6 @@ const DEFAULT_ENCODINGS: {[key: string]: CoreMouseEncoding} = {
|
|||||||
// FIXED: params = params.map(v => (v > 255) ? 0 : value);
|
// FIXED: params = params.map(v => (v > 255) ? 0 : value);
|
||||||
return `\x1b[M${S(params[0])}${S(params[1])}${S(params[2])}`;
|
return `\x1b[M${S(params[0])}${S(params[1])}${S(params[2])}`;
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* UTF8 - CSI M Pb Px Py
|
|
||||||
* Same as DEFAULT, but with optional 2-byte UTF8
|
|
||||||
* encoding for values > 223 (can encode up to 2015).
|
|
||||||
*/
|
|
||||||
UTF8: (e: ICoreMouseEvent) => {
|
|
||||||
let params = [eventCode(e, false) + 32, e.col + 32, e.row + 32];
|
|
||||||
// limit to 2-byte UTF8
|
|
||||||
params = params.map(v => (v > 2047) ? 0 : v);
|
|
||||||
return `\x1b[M${S(params[0])}${S(params[1])}${S(params[2])}`;
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* SGR - CSI < Pb ; Px ; Py M|m
|
* SGR - CSI < Pb ; Px ; Py M|m
|
||||||
* No encoding limitation.
|
* No encoding limitation.
|
||||||
@@ -151,14 +140,6 @@ const DEFAULT_ENCODINGS: {[key: string]: CoreMouseEncoding} = {
|
|||||||
SGR: (e: ICoreMouseEvent) => {
|
SGR: (e: ICoreMouseEvent) => {
|
||||||
const final = (e.action === CoreMouseAction.UP && e.button !== CoreMouseButton.WHEEL) ? 'm' : 'M';
|
const final = (e.action === CoreMouseAction.UP && e.button !== CoreMouseButton.WHEEL) ? 'm' : 'M';
|
||||||
return `\x1b[<${eventCode(e, true)};${e.col};${e.row}${final}`;
|
return `\x1b[<${eventCode(e, true)};${e.col};${e.row}${final}`;
|
||||||
},
|
|
||||||
/**
|
|
||||||
* URXVT - CSI Pb ; Px ; Py M
|
|
||||||
* Same button encoding as default, decimal encoding for coords.
|
|
||||||
* Ambiguity with other sequences, should not be used.
|
|
||||||
*/
|
|
||||||
URXVT: (e: ICoreMouseEvent) => {
|
|
||||||
return `\x1b[${eventCode(e, false) + 32};${e.col};${e.row}M`;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -167,7 +148,7 @@ const DEFAULT_ENCODINGS: {[key: string]: CoreMouseEncoding} = {
|
|||||||
*
|
*
|
||||||
* Provides mouse tracking reports with different protocols and encodings.
|
* Provides mouse tracking reports with different protocols and encodings.
|
||||||
* - protocols: NONE (default), X10, VT200, DRAG, ANY
|
* - protocols: NONE (default), X10, VT200, DRAG, ANY
|
||||||
* - encodings: DEFAULT, SGR, UTF8, URXVT
|
* - encodings: DEFAULT, SGR (UTF8, URXVT removed in #2507)
|
||||||
*
|
*
|
||||||
* Custom protocols/encodings can be added by `addProtocol` / `addEncoding`.
|
* Custom protocols/encodings can be added by `addProtocol` / `addEncoding`.
|
||||||
* To activate a protocol/encoding, set `activeProtocol` / `activeEncoding`.
|
* To activate a protocol/encoding, set `activeProtocol` / `activeEncoding`.
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export class CoreService implements ICoreService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fire onData API
|
// Fire onData API
|
||||||
this._logService.debug('sending data', data);
|
this._logService.debug(`sending data "${data}"`, () => data.split('').map(e => e.charCodeAt(0)));
|
||||||
this._onData.fire(data);
|
this._onData.fire(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,14 @@
|
|||||||
|
|
||||||
import { ILogService, IOptionsService } from 'common/services/Services';
|
import { ILogService, IOptionsService } from 'common/services/Services';
|
||||||
|
|
||||||
|
type LogType = (message?: any, ...optionalParams: any[]) => void;
|
||||||
|
|
||||||
interface IConsole {
|
interface IConsole {
|
||||||
log(message?: any, ...optionalParams: any[]): void;
|
log: LogType;
|
||||||
error(message?: any, ...optionalParams: any[]): void;
|
error: LogType;
|
||||||
info(message?: any, ...optionalParams: any[]): void;
|
info: LogType;
|
||||||
trace(message?: any, ...optionalParams: any[]): void;
|
trace: LogType;
|
||||||
warn(message?: any, ...optionalParams: any[]): void;
|
warn: LogType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// console is available on both node.js and browser contexts but the common
|
// console is available on both node.js and browser contexts but the common
|
||||||
@@ -56,27 +58,40 @@ export class LogService implements ILogService {
|
|||||||
this._logLevel = optionsKeyToLogLevel[this._optionsService.options.logLevel];
|
this._logLevel = optionsKeyToLogLevel[this._optionsService.options.logLevel];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _evalLazyOptionalParams(optionalParams: any[]): void {
|
||||||
|
for (let i = 0; i < optionalParams.length; i++) {
|
||||||
|
if (typeof optionalParams[i] === 'function') {
|
||||||
|
optionalParams[i] = optionalParams[i]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _log(type: LogType, message: string, optionalParams: any[]): void {
|
||||||
|
this._evalLazyOptionalParams(optionalParams);
|
||||||
|
type.call(console, LOG_PREFIX + message, ...optionalParams);
|
||||||
|
}
|
||||||
|
|
||||||
debug(message: string, ...optionalParams: any[]): void {
|
debug(message: string, ...optionalParams: any[]): void {
|
||||||
if (this._logLevel <= LogLevel.DEBUG) {
|
if (this._logLevel <= LogLevel.DEBUG) {
|
||||||
console.log.call(console, LOG_PREFIX + message, ...optionalParams);
|
this._log(console.log, message, optionalParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info(message: string, ...optionalParams: any[]): void {
|
info(message: string, ...optionalParams: any[]): void {
|
||||||
if (this._logLevel <= LogLevel.INFO) {
|
if (this._logLevel <= LogLevel.INFO) {
|
||||||
console.info.call(console, LOG_PREFIX + message, ...optionalParams);
|
this._log(console.info, message, optionalParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
warn(message: string, ...optionalParams: any[]): void {
|
warn(message: string, ...optionalParams: any[]): void {
|
||||||
if (this._logLevel <= LogLevel.WARN) {
|
if (this._logLevel <= LogLevel.WARN) {
|
||||||
console.warn.call(console, LOG_PREFIX + message, ...optionalParams);
|
this._log(console.warn, message, optionalParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
error(message: string, ...optionalParams: any[]): void {
|
error(message: string, ...optionalParams: any[]): void {
|
||||||
if (this._logLevel <= LogLevel.ERROR) {
|
if (this._logLevel <= LogLevel.ERROR) {
|
||||||
console.error.call(console, LOG_PREFIX + message, ...optionalParams);
|
this._log(console.error, message, optionalParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { clone } from 'common/Clone';
|
|||||||
// This sound is released under the Creative Commons Attribution 3.0 Unported
|
// This sound is released under the Creative Commons Attribution 3.0 Unported
|
||||||
// (CC BY 3.0) license. It was created by 'altemark'. No modifications have been
|
// (CC BY 3.0) license. It was created by 'altemark'. No modifications have been
|
||||||
// made, apart from the conversion to base64.
|
// made, apart from the conversion to base64.
|
||||||
export const DEFAULT_BELL_SOUND = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg==';
|
export const DEFAULT_BELL_SOUND = 'data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjMyLjEwNAAAAAAAAAAAAAAA//tQxAADB8AhSmxhIIEVCSiJrDCQBTcu3UrAIwUdkRgQbFAZC1CQEwTJ9mjRvBA4UOLD8nKVOWfh+UlK3z/177OXrfOdKl7pyn3Xf//WreyTRUoAWgBgkOAGbZHBgG1OF6zM82DWbZaUmMBptgQhGjsyYqc9ae9XFz280948NMBWInljyzsNRFLPWdnZGWrddDsjK1unuSrVN9jJsK8KuQtQCtMBjCEtImISdNKJOopIpBFpNSMbIHCSRpRR5iakjTiyzLhchUUBwCgyKiweBv/7UsQbg8isVNoMPMjAAAA0gAAABEVFGmgqK////9bP/6XCykxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq';
|
||||||
|
|
||||||
// TODO: Freeze?
|
// TODO: Freeze?
|
||||||
export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
|
export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
|
||||||
@@ -23,6 +23,8 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
|
|||||||
bellSound: DEFAULT_BELL_SOUND,
|
bellSound: DEFAULT_BELL_SOUND,
|
||||||
bellStyle: 'none',
|
bellStyle: 'none',
|
||||||
drawBoldTextInBrightColors: true,
|
drawBoldTextInBrightColors: true,
|
||||||
|
fastScrollModifier: 'alt',
|
||||||
|
fastScrollSensitivity: 5,
|
||||||
fontFamily: 'courier-new, courier, monospace',
|
fontFamily: 'courier-new, courier, monospace',
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
@@ -31,6 +33,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
|
|||||||
letterSpacing: 0,
|
letterSpacing: 0,
|
||||||
logLevel: 'info',
|
logLevel: 'info',
|
||||||
scrollback: 1000,
|
scrollback: 1000,
|
||||||
|
scrollSensitivity: 1,
|
||||||
screenReaderMode: false,
|
screenReaderMode: false,
|
||||||
macOptionIsMeta: false,
|
macOptionIsMeta: false,
|
||||||
macOptionClickForcesSelection: false,
|
macOptionClickForcesSelection: false,
|
||||||
@@ -47,7 +50,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
|
|||||||
screenKeys: false,
|
screenKeys: false,
|
||||||
cancelEvents: false,
|
cancelEvents: false,
|
||||||
useFlowControl: false,
|
useFlowControl: false,
|
||||||
wordSeparator: ' ()[]{}\'"'
|
wordSeparator: ' ()[]{}\',:;"'
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,6 +122,12 @@ export class OptionsService implements IOptionsService {
|
|||||||
throw new Error(`${key} cannot be less than 0, value: ${value}`);
|
throw new Error(`${key} cannot be less than 0, value: ${value}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'fastScrollSensitivity':
|
||||||
|
case 'scrollSensitivity':
|
||||||
|
if (value <= 0) {
|
||||||
|
throw new Error(`${key} cannot be less than or equal to 0, value: ${value}`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,6 +180,8 @@ export interface IPartialTerminalOptions {
|
|||||||
cursorStyle?: 'block' | 'underline' | 'bar';
|
cursorStyle?: 'block' | 'underline' | 'bar';
|
||||||
disableStdin?: boolean;
|
disableStdin?: boolean;
|
||||||
drawBoldTextInBrightColors?: boolean;
|
drawBoldTextInBrightColors?: boolean;
|
||||||
|
fastScrollModifier?: 'alt' | 'ctrl' | 'shift';
|
||||||
|
fastScrollSensitivity?: number;
|
||||||
fontSize?: number;
|
fontSize?: number;
|
||||||
fontFamily?: string;
|
fontFamily?: string;
|
||||||
fontWeight?: FontWeight;
|
fontWeight?: FontWeight;
|
||||||
@@ -194,6 +196,7 @@ export interface IPartialTerminalOptions {
|
|||||||
rows?: number;
|
rows?: number;
|
||||||
screenReaderMode?: boolean;
|
screenReaderMode?: boolean;
|
||||||
scrollback?: number;
|
scrollback?: number;
|
||||||
|
scrollSensitivity?: number;
|
||||||
tabStopWidth?: number;
|
tabStopWidth?: number;
|
||||||
theme?: ITheme;
|
theme?: ITheme;
|
||||||
windowsMode?: boolean;
|
windowsMode?: boolean;
|
||||||
@@ -209,6 +212,8 @@ export interface ITerminalOptions {
|
|||||||
cursorStyle: 'block' | 'underline' | 'bar';
|
cursorStyle: 'block' | 'underline' | 'bar';
|
||||||
disableStdin: boolean;
|
disableStdin: boolean;
|
||||||
drawBoldTextInBrightColors: boolean;
|
drawBoldTextInBrightColors: boolean;
|
||||||
|
fastScrollModifier: 'alt' | 'ctrl' | 'shift' | undefined;
|
||||||
|
fastScrollSensitivity: number;
|
||||||
fontSize: number;
|
fontSize: number;
|
||||||
fontFamily: string;
|
fontFamily: string;
|
||||||
fontWeight: FontWeight;
|
fontWeight: FontWeight;
|
||||||
@@ -223,6 +228,7 @@ export interface ITerminalOptions {
|
|||||||
rows: number;
|
rows: number;
|
||||||
screenReaderMode: boolean;
|
screenReaderMode: boolean;
|
||||||
scrollback: number;
|
scrollback: number;
|
||||||
|
scrollSensitivity: number;
|
||||||
tabStopWidth: number;
|
tabStopWidth: number;
|
||||||
theme: ITheme;
|
theme: ITheme;
|
||||||
windowsMode: boolean;
|
windowsMode: boolean;
|
||||||
|
|||||||
+12
-11
@@ -33,14 +33,14 @@ export class Terminal implements ITerminalApi {
|
|||||||
public get onRender(): IEvent<{ start: number, end: number }> { return this._core.onRender; }
|
public get onRender(): IEvent<{ start: number, end: number }> { return this._core.onRender; }
|
||||||
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; }
|
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; }
|
||||||
|
|
||||||
public get element(): HTMLElement { return this._core.element; }
|
public get element(): HTMLElement | undefined { return this._core.element; }
|
||||||
public get parser(): IParser {
|
public get parser(): IParser {
|
||||||
if (!this._parser) {
|
if (!this._parser) {
|
||||||
this._parser = new ParserApi(this._core);
|
this._parser = new ParserApi(this._core);
|
||||||
}
|
}
|
||||||
return this._parser;
|
return this._parser;
|
||||||
}
|
}
|
||||||
public get textarea(): HTMLTextAreaElement { return this._core.textarea; }
|
public get textarea(): HTMLTextAreaElement | undefined { return this._core.textarea; }
|
||||||
public get rows(): number { return this._core.rows; }
|
public get rows(): number { return this._core.rows; }
|
||||||
public get cols(): number { return this._core.cols; }
|
public get cols(): number { return this._core.cols; }
|
||||||
public get buffer(): IBufferApi { return new BufferApiView(this._core.buffer); }
|
public get buffer(): IBufferApi { return new BufferApiView(this._core.buffer); }
|
||||||
@@ -55,9 +55,6 @@ export class Terminal implements ITerminalApi {
|
|||||||
this._verifyIntegers(columns, rows);
|
this._verifyIntegers(columns, rows);
|
||||||
this._core.resize(columns, rows);
|
this._core.resize(columns, rows);
|
||||||
}
|
}
|
||||||
public writeln(data: string): void {
|
|
||||||
this._core.writeln(data);
|
|
||||||
}
|
|
||||||
public open(parent: HTMLElement): void {
|
public open(parent: HTMLElement): void {
|
||||||
this._core.open(parent);
|
this._core.open(parent);
|
||||||
}
|
}
|
||||||
@@ -128,11 +125,15 @@ export class Terminal implements ITerminalApi {
|
|||||||
public clear(): void {
|
public clear(): void {
|
||||||
this._core.clear();
|
this._core.clear();
|
||||||
}
|
}
|
||||||
public write(data: string): void {
|
public write(data: string | Uint8Array, callback?: () => void): void {
|
||||||
this._core.write(data);
|
this._core.write(data, callback);
|
||||||
}
|
}
|
||||||
public writeUtf8(data: Uint8Array): void {
|
public writeUtf8(data: Uint8Array, callback?: () => void): void {
|
||||||
this._core.writeUtf8(data);
|
this._core.write(data, callback);
|
||||||
|
}
|
||||||
|
public writeln(data: string | Uint8Array, callback?: () => void): void {
|
||||||
|
this._core.write(data);
|
||||||
|
this._core.write('\r\n', callback);
|
||||||
}
|
}
|
||||||
public paste(data: string): void {
|
public paste(data: string): void {
|
||||||
this._core.paste(data);
|
this._core.paste(data);
|
||||||
@@ -177,8 +178,8 @@ export class Terminal implements ITerminalApi {
|
|||||||
|
|
||||||
private _verifyIntegers(...values: number[]): void {
|
private _verifyIntegers(...values: number[]): void {
|
||||||
values.forEach(value => {
|
values.forEach(value => {
|
||||||
if (value % 1 !== 0) {
|
if (value === Infinity || isNaN(value) || value % 1 !== 0) {
|
||||||
throw new Error('This API does not accept floating point numbers');
|
throw new Error('This API only accepts integers');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,10 +103,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
|||||||
this._cursorBlinkStateManager.dispose();
|
this._cursorBlinkStateManager.dispose();
|
||||||
this._cursorBlinkStateManager = null;
|
this._cursorBlinkStateManager = null;
|
||||||
}
|
}
|
||||||
// Request a refresh from the terminal as management of rendering is being
|
|
||||||
// moved back to the terminal
|
|
||||||
this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
|
||||||
}
|
}
|
||||||
|
// Request a refresh from the terminal as management of rendering is being
|
||||||
|
// moved back to the terminal
|
||||||
|
this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onCursorMove(): void {
|
public onCursorMove(): void {
|
||||||
@@ -148,12 +148,17 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
|||||||
this._clearCursor();
|
this._clearCursor();
|
||||||
this._ctx.save();
|
this._ctx.save();
|
||||||
this._ctx.fillStyle = this._colors.cursor.css;
|
this._ctx.fillStyle = this._colors.cursor.css;
|
||||||
this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
|
const cursorStyle = this._optionsService.options.cursorStyle;
|
||||||
|
if (cursorStyle && cursorStyle !== 'block') {
|
||||||
|
this._cursorRenderers[cursorStyle](this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
|
||||||
|
} else {
|
||||||
|
this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
|
||||||
|
}
|
||||||
this._ctx.restore();
|
this._ctx.restore();
|
||||||
this._state.x = this._bufferService.buffer.x;
|
this._state.x = this._bufferService.buffer.x;
|
||||||
this._state.y = viewportRelativeCursorY;
|
this._state.y = viewportRelativeCursorY;
|
||||||
this._state.isFocused = false;
|
this._state.isFocused = false;
|
||||||
this._state.style = this._optionsService.options.cursorStyle;
|
this._state.style = cursorStyle;
|
||||||
this._state.width = this._cell.getWidth();
|
this._state.width = this._cell.getWidth();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
`}`;
|
`}`;
|
||||||
// Cursor
|
// Cursor
|
||||||
styles +=
|
styles +=
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS} {` +
|
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||||
` outline: 1px solid ${this._colors.cursor.css};` +
|
` outline: 1px solid ${this._colors.cursor.css};` +
|
||||||
` outline-offset: -1px;` +
|
` outline-offset: -1px;` +
|
||||||
`}` +
|
`}` +
|
||||||
@@ -197,10 +197,10 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
` background-color: ${this._colors.cursor.css};` +
|
` background-color: ${this._colors.cursor.css};` +
|
||||||
` color: ${this._colors.cursorAccent.css};` +
|
` color: ${this._colors.cursorAccent.css};` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` +
|
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` +
|
||||||
` box-shadow: 1px 0 0 ${this._colors.cursor.css} inset;` +
|
` box-shadow: 1px 0 0 ${this._colors.cursor.css} inset;` +
|
||||||
`}` +
|
`}` +
|
||||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` +
|
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` +
|
||||||
` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` +
|
` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` +
|
||||||
`}`;
|
`}`;
|
||||||
// Selection
|
// Selection
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ describe('CharWidth Integration Tests', function(): void {
|
|||||||
browser = await puppeteer.launch({
|
browser = await puppeteer.launch({
|
||||||
headless: process.argv.indexOf('--headless') !== -1,
|
headless: process.argv.indexOf('--headless') !== -1,
|
||||||
slowMo: 80,
|
slowMo: 80,
|
||||||
args: [`--window-size=${width},${height}`]
|
args: [`--window-size=${width},${height}`, `--no-sandbox`]
|
||||||
});
|
});
|
||||||
page = (await browser.pages())[0];
|
page = (await browser.pages())[0];
|
||||||
await page.setViewport({ width, height });
|
await page.setViewport({ width, height });
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ describe('InputHandler Integration Tests', function(): void {
|
|||||||
browser = await puppeteer.launch({
|
browser = await puppeteer.launch({
|
||||||
headless: process.argv.indexOf('--headless') !== -1,
|
headless: process.argv.indexOf('--headless') !== -1,
|
||||||
slowMo: 80,
|
slowMo: 80,
|
||||||
args: [`--window-size=${width},${height}`]
|
args: [`--window-size=${width},${height}`, `--no-sandbox`]
|
||||||
});
|
});
|
||||||
page = (await browser.pages())[0];
|
page = (await browser.pages())[0];
|
||||||
await page.setViewport({ width, height });
|
await page.setViewport({ width, height });
|
||||||
@@ -196,6 +196,36 @@ describe('InputHandler Integration Tests', function(): void {
|
|||||||
assert.deepEqual(await getLinesAsArray(5), [' 4', ' 5', 'abc', 'def', 'ghi']);
|
assert.deepEqual(await getLinesAsArray(5), [' 4', ' 5', 'abc', 'def', 'ghi']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('IL: Insert Ps Line(s) (default = 1) - CSI Ps L', async function(): Promise<any> {
|
||||||
|
await page.evaluate(`
|
||||||
|
// Default
|
||||||
|
window.term.write('foo\x1b[La')
|
||||||
|
// Explicit
|
||||||
|
window.term.write('\x1b[2Lb')
|
||||||
|
`);
|
||||||
|
assert.deepEqual(await getLinesAsArray(4), ['b', '', 'a', 'foo']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('DL: Delete Ps Line(s) (default = 1) - CSI Ps M', async function(): Promise<any> {
|
||||||
|
await page.evaluate(`
|
||||||
|
// Default
|
||||||
|
window.term.write('a\\nb\x1b[1F\x1b[M')
|
||||||
|
// Explicit
|
||||||
|
window.term.write('\x1b[1Ed\\ne\\nf\x1b[2F\x1b[2M')
|
||||||
|
`);
|
||||||
|
assert.deepEqual(await getLinesAsArray(5), [' b', ' f', '', '', '']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('DCH: Delete Ps Character(s) (default = 1) - CSI Ps P', async function(): Promise<any> {
|
||||||
|
await page.evaluate(`
|
||||||
|
// Default
|
||||||
|
window.term.write('abc\x1b[1;1H\x1b[P')
|
||||||
|
// Explicit
|
||||||
|
window.term.write('\\n\\rdef\x1b[2;1H\x1b[2P')
|
||||||
|
`);
|
||||||
|
assert.deepEqual(await getLinesAsArray(2), ['bc', 'f']);
|
||||||
|
});
|
||||||
|
|
||||||
describe('DSR: Device Status Report', () => {
|
describe('DSR: Device Status Report', () => {
|
||||||
it('Status Report - CSI 5 n', async function(): Promise<any> {
|
it('Status Report - CSI 5 n', async function(): Promise<any> {
|
||||||
await page.evaluate(`
|
await page.evaluate(`
|
||||||
|
|||||||
+5
-1187
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@ describe('Parser Integration Tests', function(): void {
|
|||||||
browser = await puppeteer.launch({
|
browser = await puppeteer.launch({
|
||||||
headless: process.argv.indexOf('--headless') !== -1,
|
headless: process.argv.indexOf('--headless') !== -1,
|
||||||
slowMo: 80,
|
slowMo: 80,
|
||||||
args: [`--window-size=${width},${height}`]
|
args: [`--window-size=${width},${height}`, `--no-sandbox`]
|
||||||
});
|
});
|
||||||
page = (await browser.pages())[0];
|
page = (await browser.pages())[0];
|
||||||
await page.setViewport({ width, height });
|
await page.setViewport({ width, height });
|
||||||
|
|||||||
+76
-14
@@ -21,7 +21,7 @@ describe('API Integration Tests', function(): void {
|
|||||||
browser = await puppeteer.launch({
|
browser = await puppeteer.launch({
|
||||||
headless: process.argv.indexOf('--headless') !== -1,
|
headless: process.argv.indexOf('--headless') !== -1,
|
||||||
slowMo: 80,
|
slowMo: 80,
|
||||||
args: [`--window-size=${width},${height}`]
|
args: [`--window-size=${width},${height}`, `--no-sandbox`]
|
||||||
});
|
});
|
||||||
page = (await browser.pages())[0];
|
page = (await browser.pages())[0];
|
||||||
await page.setViewport({ width, height });
|
await page.setViewport({ width, height });
|
||||||
@@ -51,6 +51,44 @@ describe('API Integration Tests', function(): void {
|
|||||||
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foobar文');
|
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foobar文');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('write with callback', async function(): Promise<any> {
|
||||||
|
await openTerminal();
|
||||||
|
await page.evaluate(`
|
||||||
|
window.term.write('foo', () => { window.__x = 'a'; });
|
||||||
|
window.term.write('bar', () => { window.__x += 'b'; });
|
||||||
|
window.term.write('文', () => { window.__x += 'c'; });
|
||||||
|
`);
|
||||||
|
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foobar文');
|
||||||
|
assert.equal(await page.evaluate(`window.__x`), 'abc');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('write - bytes (UTF8)', async function(): Promise<any> {
|
||||||
|
await openTerminal();
|
||||||
|
await page.evaluate(`
|
||||||
|
// foo
|
||||||
|
window.term.write(new Uint8Array([102, 111, 111]));
|
||||||
|
// bar
|
||||||
|
window.term.write(new Uint8Array([98, 97, 114]));
|
||||||
|
// 文
|
||||||
|
window.term.write(new Uint8Array([230, 150, 135]));
|
||||||
|
`);
|
||||||
|
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foobar文');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('write - bytes (UTF8) with callback', async function(): Promise<any> {
|
||||||
|
await openTerminal();
|
||||||
|
await page.evaluate(`
|
||||||
|
// foo
|
||||||
|
window.term.write(new Uint8Array([102, 111, 111]), () => { window.__x = 'A'; });
|
||||||
|
// bar
|
||||||
|
window.term.write(new Uint8Array([98, 97, 114]), () => { window.__x += 'B'; });
|
||||||
|
// 文
|
||||||
|
window.term.write(new Uint8Array([230, 150, 135]), () => { window.__x += 'C'; });
|
||||||
|
`);
|
||||||
|
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foobar文');
|
||||||
|
assert.equal(await page.evaluate(`window.__x`), 'ABC');
|
||||||
|
});
|
||||||
|
|
||||||
it('writeln', async function(): Promise<any> {
|
it('writeln', async function(): Promise<any> {
|
||||||
await openTerminal();
|
await openTerminal();
|
||||||
await page.evaluate(`
|
await page.evaluate(`
|
||||||
@@ -63,17 +101,29 @@ describe('API Integration Tests', function(): void {
|
|||||||
assert.equal(await page.evaluate(`window.term.buffer.getLine(2).translateToString(true)`), '文');
|
assert.equal(await page.evaluate(`window.term.buffer.getLine(2).translateToString(true)`), '文');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('writeUtf8', async function(): Promise<any> {
|
it('writeln with callback', async function(): Promise<any> {
|
||||||
await openTerminal();
|
await openTerminal();
|
||||||
await page.evaluate(`
|
await page.evaluate(`
|
||||||
// foo
|
window.term.writeln('foo', () => { window.__x = '1'; });
|
||||||
window.term.writeUtf8(new Uint8Array([102, 111, 111]));
|
window.term.writeln('bar', () => { window.__x += '2'; });
|
||||||
// bar
|
window.term.writeln('文', () => { window.__x += '3'; });
|
||||||
window.term.writeUtf8(new Uint8Array([98, 97, 114]));
|
|
||||||
// 文
|
|
||||||
window.term.writeUtf8(new Uint8Array([230, 150, 135]));
|
|
||||||
`);
|
`);
|
||||||
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foobar文');
|
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo');
|
||||||
|
assert.equal(await page.evaluate(`window.term.buffer.getLine(1).translateToString(true)`), 'bar');
|
||||||
|
assert.equal(await page.evaluate(`window.term.buffer.getLine(2).translateToString(true)`), '文');
|
||||||
|
assert.equal(await page.evaluate(`window.__x`), '123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('writeln - bytes (UTF8)', async function(): Promise<any> {
|
||||||
|
await openTerminal();
|
||||||
|
await page.evaluate(`
|
||||||
|
window.term.writeln(new Uint8Array([102, 111, 111]));
|
||||||
|
window.term.writeln(new Uint8Array([98, 97, 114]));
|
||||||
|
window.term.writeln(new Uint8Array([230, 150, 135]));
|
||||||
|
`);
|
||||||
|
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo');
|
||||||
|
assert.equal(await page.evaluate(`window.term.buffer.getLine(1).translateToString(true)`), 'bar');
|
||||||
|
assert.equal(await page.evaluate(`window.term.buffer.getLine(2).translateToString(true)`), '文');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('paste', async function(): Promise<any> {
|
it('paste', async function(): Promise<any> {
|
||||||
@@ -83,11 +133,9 @@ describe('API Integration Tests', function(): void {
|
|||||||
window.term.onData(e => calls.push(e));
|
window.term.onData(e => calls.push(e));
|
||||||
window.term.paste('foo');
|
window.term.paste('foo');
|
||||||
window.term.paste('\\r\\nfoo\\nbar\\r');
|
window.term.paste('\\r\\nfoo\\nbar\\r');
|
||||||
window.term.write('\\x1b[?2004h');
|
window.term.write('\\x1b[?2004h', () => {
|
||||||
// TODO: Use promise/callback for write when we support that
|
window.term.paste('foo');
|
||||||
// Force sync write
|
});
|
||||||
window.term._core._innerWrite();
|
|
||||||
window.term.paste('foo');
|
|
||||||
`);
|
`);
|
||||||
assert.deepEqual(await page.evaluate(`window.calls`), ['foo', '\rfoo\rbar\r', '\x1b[200~foo\x1b[201~']);
|
assert.deepEqual(await page.evaluate(`window.calls`), ['foo', '\rfoo\rbar\r', '\x1b[200~foo\x1b[201~']);
|
||||||
});
|
});
|
||||||
@@ -462,6 +510,20 @@ describe('API Integration Tests', function(): void {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('dispose', async function(): Promise<any> {
|
||||||
|
await page.evaluate(`
|
||||||
|
window.term = new Terminal();
|
||||||
|
window.term.dispose();
|
||||||
|
`);
|
||||||
|
assert.equal(await page.evaluate(`window.term._core._isDisposed`), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispose (opened)', async function(): Promise<any> {
|
||||||
|
await openTerminal();
|
||||||
|
await page.evaluate(`window.term.dispose()`);
|
||||||
|
assert.equal(await page.evaluate(`window.term._core._isDisposed`), true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
|
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
|
||||||
|
|||||||
@@ -9,17 +9,6 @@ import { spawn } from 'node-pty';
|
|||||||
import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder';
|
import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder';
|
||||||
import { Terminal } from 'Terminal';
|
import { Terminal } from 'Terminal';
|
||||||
|
|
||||||
class TestTerminal extends Terminal {
|
|
||||||
writeSync(data: string): void {
|
|
||||||
this.writeBuffer.push(data);
|
|
||||||
this._innerWrite();
|
|
||||||
}
|
|
||||||
writeSyncUtf8(data: Uint8Array): void {
|
|
||||||
this.writeBufferUtf8.push(data);
|
|
||||||
this._innerWriteUtf8();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
perfContext('Terminal: ls -lR /usr/lib', () => {
|
perfContext('Terminal: ls -lR /usr/lib', () => {
|
||||||
let content = '';
|
let content = '';
|
||||||
let contentUtf8: Uint8Array;
|
let contentUtf8: Uint8Array;
|
||||||
@@ -56,9 +45,9 @@ perfContext('Terminal: ls -lR /usr/lib', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
perfContext('write', () => {
|
perfContext('write', () => {
|
||||||
let terminal: TestTerminal;
|
let terminal: Terminal;
|
||||||
before(() => {
|
before(() => {
|
||||||
terminal = new TestTerminal({cols: 80, rows: 25, scrollback: 1000});
|
terminal = new Terminal({cols: 80, rows: 25, scrollback: 1000});
|
||||||
});
|
});
|
||||||
new ThroughputRuntimeCase('', () => {
|
new ThroughputRuntimeCase('', () => {
|
||||||
terminal.writeSync(content);
|
terminal.writeSync(content);
|
||||||
@@ -67,12 +56,12 @@ perfContext('Terminal: ls -lR /usr/lib', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
perfContext('writeUtf8', () => {
|
perfContext('writeUtf8', () => {
|
||||||
let terminal: TestTerminal;
|
let terminal: Terminal;
|
||||||
before(() => {
|
before(() => {
|
||||||
terminal = new TestTerminal({cols: 80, rows: 25, scrollback: 1000});
|
terminal = new Terminal({cols: 80, rows: 25, scrollback: 1000});
|
||||||
});
|
});
|
||||||
new ThroughputRuntimeCase('', () => {
|
new ThroughputRuntimeCase('', () => {
|
||||||
terminal.writeSyncUtf8(contentUtf8);
|
terminal.writeSync(content);
|
||||||
return {payloadSize: contentUtf8.length};
|
return {payloadSize: contentUtf8.length};
|
||||||
}, {fork: false}).showAverageThroughput();
|
}, {fork: false}).showAverageThroughput();
|
||||||
});
|
});
|
||||||
|
|||||||
Vendored
+76
-16
@@ -82,6 +82,16 @@ declare module 'xterm' {
|
|||||||
*/
|
*/
|
||||||
drawBoldTextInBrightColors?: boolean;
|
drawBoldTextInBrightColors?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The modifier key hold to multiply scroll speed.
|
||||||
|
*/
|
||||||
|
fastScrollModifier?: 'alt' | 'ctrl' | 'shift' | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The scroll speed multiplier used for fast scrolling.
|
||||||
|
*/
|
||||||
|
fastScrollSensitivity?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The font size used to render text.
|
* The font size used to render text.
|
||||||
*/
|
*/
|
||||||
@@ -173,6 +183,11 @@ declare module 'xterm' {
|
|||||||
*/
|
*/
|
||||||
scrollback?: number;
|
scrollback?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The scrolling speed multiplier used for adjusting normal scrolling speed.
|
||||||
|
*/
|
||||||
|
scrollSensitivity?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The size of tab stops in the terminal.
|
* The size of tab stops in the terminal.
|
||||||
*/
|
*/
|
||||||
@@ -269,7 +284,7 @@ declare module 'xterm' {
|
|||||||
/**
|
/**
|
||||||
* A callback that fires when the mouse hovers over a link for a moment.
|
* A callback that fires when the mouse hovers over a link for a moment.
|
||||||
*/
|
*/
|
||||||
tooltipCallback?: (event: MouseEvent, uri: string) => boolean | void;
|
tooltipCallback?: (event: MouseEvent, uri: string, location: IViewportRange) => boolean | void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A callback that fires when the mouse leaves a link. Note that this can
|
* A callback that fires when the mouse leaves a link. Note that this can
|
||||||
@@ -310,7 +325,8 @@ declare module 'xterm' {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a specific line in the terminal that is tracked when scrollback
|
* Represents a specific line in the terminal that is tracked when scrollback
|
||||||
* is trimmed and lines are added or removed.
|
* is trimmed and lines are added or removed. This is a single line that may
|
||||||
|
* be part of a larger wrapped line.
|
||||||
*/
|
*/
|
||||||
export interface IMarker extends IDisposable {
|
export interface IMarker extends IDisposable {
|
||||||
/**
|
/**
|
||||||
@@ -324,7 +340,8 @@ declare module 'xterm' {
|
|||||||
readonly isDisposed: boolean;
|
readonly isDisposed: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The actual line index in the buffer at this point in time.
|
* The actual line index in the buffer at this point in time. This is set to
|
||||||
|
* -1 if the marker has been disposed.
|
||||||
*/
|
*/
|
||||||
readonly line: number;
|
readonly line: number;
|
||||||
}
|
}
|
||||||
@@ -352,12 +369,12 @@ declare module 'xterm' {
|
|||||||
/**
|
/**
|
||||||
* The element containing the terminal.
|
* The element containing the terminal.
|
||||||
*/
|
*/
|
||||||
readonly element: HTMLElement;
|
readonly element: HTMLElement | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The textarea that accepts input for the terminal.
|
* The textarea that accepts input for the terminal.
|
||||||
*/
|
*/
|
||||||
readonly textarea: HTMLTextAreaElement;
|
readonly textarea: HTMLTextAreaElement | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of rows in the terminal's viewport. Use
|
* The number of rows in the terminal's viewport. Use
|
||||||
@@ -434,7 +451,7 @@ declare module 'xterm' {
|
|||||||
onLineFeed: IEvent<void>;
|
onLineFeed: IEvent<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an event listener for when a scroll occurs. The event value is the
|
* Adds an event listener for when a scroll occurs. The event value is the
|
||||||
* new position of the viewport.
|
* new position of the viewport.
|
||||||
* @returns an `IDisposable` to stop listening.
|
* @returns an `IDisposable` to stop listening.
|
||||||
*/
|
*/
|
||||||
@@ -650,24 +667,32 @@ declare module 'xterm' {
|
|||||||
clear(): void;
|
clear(): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes text to the terminal.
|
* Write data to the terminal.
|
||||||
* @param data The text to write to the terminal.
|
* @param data The data to write to the terminal. This can either be raw
|
||||||
|
* bytes given as Uint8Array from the pty or a string. Raw bytes will always
|
||||||
|
* be treated as UTF-8 encoded, string data as UTF-16.
|
||||||
|
* @param callback Optional callback that fires when the data was processed
|
||||||
|
* by the parser.
|
||||||
*/
|
*/
|
||||||
write(data: string): void;
|
write(data: string | Uint8Array, callback?: () => void): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes text to the terminal, followed by a break line character (\n).
|
* Writes data to the terminal, followed by a break line character (\n).
|
||||||
* @param data The text to write to the terminal.
|
* @param data The data to write to the terminal. This can either be raw
|
||||||
|
* bytes given as Uint8Array from the pty or a string. Raw bytes will always
|
||||||
|
* be treated as UTF-8 encoded, string data as UTF-16.
|
||||||
|
* @param callback Optional callback that fires when the data was processed
|
||||||
|
* by the parser.
|
||||||
*/
|
*/
|
||||||
writeln(data: string): void;
|
writeln(data: string | Uint8Array, callback?: () => void): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes UTF8 data to the terminal. This has a slight performance advantage
|
* Write UTF8 data to the terminal.
|
||||||
* over the string based write method due to lesser data conversions needed
|
|
||||||
* on the way from the pty to xterm.js.
|
|
||||||
* @param data The data to write to the terminal.
|
* @param data The data to write to the terminal.
|
||||||
|
* @param callback Optional callback when data was processed.
|
||||||
|
* @deprecated use `write` instead
|
||||||
*/
|
*/
|
||||||
writeUtf8(data: Uint8Array): void;
|
writeUtf8(data: Uint8Array, callback?: () => void): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes text to the terminal, performing the necessary transformations for pasted text.
|
* Writes text to the terminal, performing the necessary transformations for pasted text.
|
||||||
@@ -834,6 +859,41 @@ declare module 'xterm' {
|
|||||||
endRow: number;
|
endRow: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object representing a range within the viewport of the terminal.
|
||||||
|
*/
|
||||||
|
export interface IViewportRange {
|
||||||
|
/**
|
||||||
|
* The start of the range.
|
||||||
|
*/
|
||||||
|
start: IViewportRangePosition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The end of the range.
|
||||||
|
*/
|
||||||
|
end: IViewportRangePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object representing a cell position within the viewport of the terminal.
|
||||||
|
*/
|
||||||
|
interface IViewportRangePosition {
|
||||||
|
/**
|
||||||
|
* The x position of the cell. This is a 0-based index that refers to the
|
||||||
|
* space in between columns, not the column itself. Index 0 refers to the
|
||||||
|
* left side of the viewport, index `Terminal.cols` refers to the right side
|
||||||
|
* of the viewport. This can be thought of as how a cursor is positioned in
|
||||||
|
* a text editor.
|
||||||
|
*/
|
||||||
|
x: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The y position of the cell. This is a 0-based index that refers to a
|
||||||
|
* specific row.
|
||||||
|
*/
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a terminal buffer.
|
* Represents a terminal buffer.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ module.exports = {
|
|||||||
output: {
|
output: {
|
||||||
filename: 'xterm.js',
|
filename: 'xterm.js',
|
||||||
path: path.resolve('./lib'),
|
path: path.resolve('./lib'),
|
||||||
library: 'Terminal',
|
|
||||||
libraryTarget: 'umd'
|
libraryTarget: 'umd'
|
||||||
},
|
},
|
||||||
mode: 'production'
|
mode: 'production'
|
||||||
|
|||||||
@@ -4719,7 +4719,12 @@ typedarray@^0.0.6:
|
|||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||||
|
|
||||||
typescript@3.5, typescript@^3.5.1:
|
typescript@3.6:
|
||||||
|
version "3.6.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da"
|
||||||
|
integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==
|
||||||
|
|
||||||
|
typescript@^3.5.1:
|
||||||
version "3.5.1"
|
version "3.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.1.tgz#ba72a6a600b2158139c5dd8850f700e231464202"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.1.tgz#ba72a6a600b2158139c5dd8850f700e231464202"
|
||||||
integrity sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw==
|
integrity sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw==
|
||||||
|
|||||||
Reference in New Issue
Block a user