Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a11ba0a89c | ||
|
|
2134e050cb | ||
|
|
e2ed603ab8 | ||
|
|
ff3781a431 | ||
|
|
55cd97ec68 | ||
|
|
2f0a4fc5c0 | ||
|
|
6dd61f96c0 | ||
|
|
fdae61380c | ||
|
|
09acb2b5f9 | ||
|
|
19c9fef8e2 | ||
|
|
c335682559 | ||
|
|
96806ee384 | ||
|
|
096655a3eb | ||
|
|
a9788c364d | ||
|
|
f593d14823 | ||
|
|
6801793547 | ||
|
|
f52d631fa4 | ||
|
|
ca70d7b6b4 | ||
|
|
fe1d2f6af1 | ||
|
|
64b37ef518 | ||
|
|
654a0a671d | ||
|
|
dc1b1b4537 | ||
|
|
579e43c4cc | ||
|
|
f8d8288e0e | ||
|
|
ea7f89842a | ||
|
|
91a79e9dee | ||
|
|
a385ab0802 | ||
|
|
9175a220be | ||
|
|
0131fc9ebf | ||
|
|
c9eab73bd4 | ||
|
|
e42e20f776 | ||
|
|
d046e2a77f | ||
|
|
873dd76167 | ||
|
|
6824d6f4df | ||
|
|
74edc6fdd4 | ||
|
|
99926e67cd | ||
|
|
de712d9b43 | ||
|
|
01f68c50ed | ||
|
|
840774c034 | ||
|
|
da898b7efa | ||
|
|
d4f4e19056 | ||
|
|
94ba682b57 | ||
|
|
8cc6df1f70 | ||
|
|
7a0b462e7e | ||
|
|
d229b775ab | ||
|
|
e91d90dbbc | ||
|
|
8c65fcacb3 | ||
|
|
e55b9fe922 |
@@ -1,4 +1,4 @@
|
|||||||
FROM node:10
|
FROM node:14
|
||||||
|
|
||||||
# Configure apt
|
# Configure apt
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|||||||
+2
-1
@@ -43,10 +43,11 @@
|
|||||||
"@typescript-eslint/array-type": [
|
"@typescript-eslint/array-type": [
|
||||||
"warn",
|
"warn",
|
||||||
{
|
{
|
||||||
"default": "array-simple",
|
"default": "array",
|
||||||
"readonly": "generic"
|
"readonly": "generic"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"@typescript-eslint/consistent-type-assertions": "warn",
|
||||||
"@typescript-eslint/consistent-type-definitions": "warn",
|
"@typescript-eslint/consistent-type-definitions": "warn",
|
||||||
"@typescript-eslint/explicit-function-return-type": [
|
"@typescript-eslint/explicit-function-return-type": [
|
||||||
"warn",
|
"warn",
|
||||||
|
|||||||
@@ -143,7 +143,6 @@ Xterm.js is used in several world-class applications to provide great terminal e
|
|||||||
- [**cPanel & WHM**](https://cpanel.com): The hosting platform of choice.
|
- [**cPanel & WHM**](https://cpanel.com): The hosting platform of choice.
|
||||||
- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to xterm.js.
|
- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to xterm.js.
|
||||||
- [**SSH Web Client**](https://github.com/roke22/PHP-SSH2-Web-Client): SSH Web Client with PHP.
|
- [**SSH Web Client**](https://github.com/roke22/PHP-SSH2-Web-Client): SSH Web Client with PHP.
|
||||||
- [**Shellvault**](https://www.shellvault.io): The cloud-based SSH terminal you can access from anywhere.
|
|
||||||
- [**Juno**](http://junolab.org/): A flexible Julia IDE, based on Atom.
|
- [**Juno**](http://junolab.org/): A flexible Julia IDE, based on Atom.
|
||||||
- [**webssh**](https://github.com/huashengdun/webssh): Web based ssh client.
|
- [**webssh**](https://github.com/huashengdun/webssh): Web based ssh client.
|
||||||
- [**info-beamer hosted**](https://info-beamer.com): Uses xterm.js to manage digital signage devices from the web dashboard.
|
- [**info-beamer hosted**](https://info-beamer.com): Uses xterm.js to manage digital signage devices from the web dashboard.
|
||||||
@@ -175,7 +174,9 @@ Xterm.js is used in several world-class applications to provide great terminal e
|
|||||||
- [**gifcast**](https://dstein64.github.io/gifcast/): Converts an asciinema cast to an animated GIF.
|
- [**gifcast**](https://dstein64.github.io/gifcast/): Converts an asciinema cast to an animated GIF.
|
||||||
- [**WizardWebssh**](https://gitlab.com/mikeramsey/wizardwebssh): A terminal with Pyqt5 Widget for embedding, which can be used as an ssh client to connect to your ssh servers. It is written in Python, based on tornado, paramiko, and xterm.js.
|
- [**WizardWebssh**](https://gitlab.com/mikeramsey/wizardwebssh): A terminal with Pyqt5 Widget for embedding, which can be used as an ssh client to connect to your ssh servers. It is written in Python, based on tornado, paramiko, and xterm.js.
|
||||||
- [**Wizard Assistant**](https://wizardassistant.com/): Wizard Assistant comes with advanced automation tools, preloaded common and special time-saving commands, and a built-in SSH terminal. Now you can remotely administer, troubleshoot, and analyze any system with ease.
|
- [**Wizard Assistant**](https://wizardassistant.com/): Wizard Assistant comes with advanced automation tools, preloaded common and special time-saving commands, and a built-in SSH terminal. Now you can remotely administer, troubleshoot, and analyze any system with ease.
|
||||||
[And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
|
- [**ucli**](https://github.com/tsadarsh/ucli): Command Line for everyone :family_man_woman_girl_boy: at [www.ucli.tech](https://www.ucli.tech).
|
||||||
|
- [**Tess**](https://github.com/SquitchYT/Tess/): Simple Terminal Fully Customizable for Everyone.
|
||||||
|
- [And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
|
||||||
|
|
||||||
Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it on our list. Note: Please add any new contributions to the end of the list only.
|
Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it on our list. Note: Please add any new contributions to the end of the list only.
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export class AttachAddon implements ITerminalAddon {
|
|||||||
this._socket = socket;
|
this._socket = socket;
|
||||||
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public activate(terminal: Terminal): void {
|
public activate(terminal: Terminal): void {
|
||||||
|
|||||||
@@ -15,6 +15,15 @@ interface IFontMetadata {
|
|||||||
blob: () => Promise<Blob>;
|
blob: () => Promise<Blob>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IFontAccessNavigator {
|
||||||
|
fonts: {
|
||||||
|
query: () => Promise<IFontMetadata[]>;
|
||||||
|
};
|
||||||
|
permissions: {
|
||||||
|
request?: (permission: { name: string }) => Promise<{state: string}>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let fontsPromise: Promise<FontList | Record<string, IFontMetadata[]>> | undefined = undefined;
|
let fontsPromise: Promise<FontList | Record<string, IFontMetadata[]>> | undefined = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,7 +37,7 @@ export default async function load(fontFamily: string, cacheSize: number): Promi
|
|||||||
// Web environment that supports font access API
|
// Web environment that supports font access API
|
||||||
if (typeof navigator !== 'undefined' && 'fonts' in navigator) {
|
if (typeof navigator !== 'undefined' && 'fonts' in navigator) {
|
||||||
try {
|
try {
|
||||||
const status = await (navigator as any).permissions.request?.({
|
const status = await (navigator as IFontAccessNavigator).permissions.request?.({
|
||||||
name: 'local-fonts'
|
name: 'local-fonts'
|
||||||
});
|
});
|
||||||
if (status && status.state !== 'granted') {
|
if (status && status.state !== 'granted') {
|
||||||
@@ -44,8 +53,8 @@ export default async function load(fontFamily: string, cacheSize: number): Promi
|
|||||||
}
|
}
|
||||||
const fonts: Record<string, IFontMetadata[]> = {};
|
const fonts: Record<string, IFontMetadata[]> = {};
|
||||||
try {
|
try {
|
||||||
const fontsIterator: AsyncIterableIterator<IFontMetadata> = (navigator as any).fonts.query();
|
const fontsIterator = await (navigator as IFontAccessNavigator).fonts.query();
|
||||||
for await (const metadata of fontsIterator) {
|
for (const metadata of fontsIterator) {
|
||||||
if (!fonts.hasOwnProperty(metadata.family)) {
|
if (!fonts.hasOwnProperty(metadata.family)) {
|
||||||
fonts[metadata.family] = [];
|
fonts[metadata.family] = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -394,7 +394,7 @@ export class SearchAddon implements ITerminalAddon {
|
|||||||
// 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.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
|
if (result.row >= (terminal.buffer.active.viewportY + terminal.rows) || result.row < terminal.buffer.active.viewportY) {
|
||||||
let scroll = result.row - terminal.buffer.active.viewportY;
|
let scroll = result.row - terminal.buffer.active.viewportY;
|
||||||
scroll = scroll - Math.floor(terminal.rows / 2);
|
scroll -= Math.floor(terminal.rows / 2);
|
||||||
terminal.scrollLines(scroll);
|
terminal.scrollLines(scroll);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ perfContext('Terminal: sh -c "dd if=/dev/urandom count=40 bs=1k | hexdump | lolc
|
|||||||
chunks.push(data as unknown as Buffer);
|
chunks.push(data as unknown as Buffer);
|
||||||
length += data.length;
|
length += data.length;
|
||||||
});
|
});
|
||||||
await new Promise(resolve => p.on('exit', () => resolve()));
|
await new Promise<void>(resolve => p.on('exit', () => resolve()));
|
||||||
contentUtf8 = Buffer.concat(chunks, length);
|
contentUtf8 = Buffer.concat(chunks, length);
|
||||||
// translate to content string
|
// translate to content string
|
||||||
const buffer = new Uint32Array(contentUtf8.length);
|
const buffer = new Uint32Array(contentUtf8.length);
|
||||||
|
|||||||
@@ -487,9 +487,9 @@ function newArray<T>(initial: T | ((index: number) => T), count: number): T[] {
|
|||||||
const array: T[] = new Array<T>(count);
|
const array: T[] = new Array<T>(count);
|
||||||
for (let i = 0; i < array.length; i++) {
|
for (let i = 0; i < array.length; i++) {
|
||||||
if (typeof initial === 'function') {
|
if (typeof initial === 'function') {
|
||||||
array[i] = (<(index: number) => T>initial)(i);
|
array[i] = (initial as (index: number) => T)(i);
|
||||||
} else {
|
} else {
|
||||||
array[i] = <T>initial;
|
array[i] = initial as T;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm-addon-webgl",
|
"name": "xterm-addon-webgl",
|
||||||
"version": "0.11.0",
|
"version": "0.11.1",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "The xterm.js authors",
|
"name": "The xterm.js authors",
|
||||||
"url": "https://xtermjs.org/"
|
"url": "https://xtermjs.org/"
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ export class GlyphRenderer {
|
|||||||
// Get attributes from fg (excluding inverse) and resolve inverse by pullibng rgb colors
|
// Get attributes from fg (excluding inverse) and resolve inverse by pullibng rgb colors
|
||||||
// from bg. This is needed since the inverse fg color should be based on the original bg
|
// from bg. This is needed since the inverse fg color should be based on the original bg
|
||||||
// color, not on the selection color
|
// color, not on the selection color
|
||||||
fg = (fg & ~(Attributes.CM_MASK | Attributes.RGB_MASK | FgFlags.INVERSE));
|
fg &= ~(Attributes.CM_MASK | Attributes.RGB_MASK | FgFlags.INVERSE);
|
||||||
switch (workCell.getBgColorMode()) {
|
switch (workCell.getBgColorMode()) {
|
||||||
case Attributes.CM_P16:
|
case Attributes.CM_P16:
|
||||||
case Attributes.CM_P256:
|
case Attributes.CM_P256:
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ export class WebglAddon implements ITerminalAddon {
|
|||||||
throw new Error('Cannot activate WebglAddon before Terminal.open');
|
throw new Error('Cannot activate WebglAddon before Terminal.open');
|
||||||
}
|
}
|
||||||
this._terminal = terminal;
|
this._terminal = terminal;
|
||||||
const renderService: IRenderService = (<any>terminal)._core._renderService;
|
const renderService: IRenderService = (terminal as any)._core._renderService;
|
||||||
const characterJoinerService: ICharacterJoinerService = (<any>terminal)._core._characterJoinerService;
|
const characterJoinerService: ICharacterJoinerService = (terminal as any)._core._characterJoinerService;
|
||||||
const colors: IColorSet = (<any>terminal)._core._colorManager.colors;
|
const colors: IColorSet = (terminal as any)._core._colorManager.colors;
|
||||||
this._renderer = new WebglRenderer(terminal, colors, characterJoinerService, this._preserveDrawingBuffer);
|
this._renderer = new WebglRenderer(terminal, colors, characterJoinerService, this._preserveDrawingBuffer);
|
||||||
this._renderer.onContextLoss(() => this._onContextLoss.fire());
|
this._renderer.onContextLoss(() => this._onContextLoss.fire());
|
||||||
renderService.setRenderer(this._renderer);
|
renderService.setRenderer(this._renderer);
|
||||||
|
|||||||
@@ -342,7 +342,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
|
|
||||||
// Flag combined chars with a bit mask so they're easily identifiable
|
// Flag combined chars with a bit mask so they're easily identifiable
|
||||||
if (chars.length > 1) {
|
if (chars.length > 1) {
|
||||||
code = code | COMBINED_CHAR_BIT_MASK;
|
code |= COMBINED_CHAR_BIT_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache the results in the model
|
// Cache the results in the model
|
||||||
|
|||||||
+1
-2
@@ -3,8 +3,7 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IEvent } from 'node-pty';
|
import { Terminal, ITerminalAddon, IEvent } from 'xterm';
|
||||||
import { Terminal, ITerminalAddon } from 'xterm';
|
|
||||||
|
|
||||||
declare module 'xterm-addon-webgl' {
|
declare module 'xterm-addon-webgl' {
|
||||||
/**
|
/**
|
||||||
|
|||||||
+7
-7
@@ -10,7 +10,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: '14.x'
|
||||||
displayName: 'Install Node.js'
|
displayName: 'Install Node.js'
|
||||||
- task: YarnInstaller@3
|
- task: YarnInstaller@3
|
||||||
inputs:
|
inputs:
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: '14.x'
|
||||||
displayName: 'Install Node.js'
|
displayName: 'Install Node.js'
|
||||||
- task: CacheBeta@1
|
- task: CacheBeta@1
|
||||||
inputs:
|
inputs:
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: '14.x'
|
||||||
displayName: 'Install Node.js'
|
displayName: 'Install Node.js'
|
||||||
- task: CacheBeta@1
|
- task: CacheBeta@1
|
||||||
inputs:
|
inputs:
|
||||||
@@ -92,7 +92,7 @@ jobs:
|
|||||||
displayName: Install required packages
|
displayName: Install required packages
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: '14.x'
|
||||||
displayName: 'Install Node.js'
|
displayName: 'Install Node.js'
|
||||||
- task: YarnInstaller@3
|
- task: YarnInstaller@3
|
||||||
inputs:
|
inputs:
|
||||||
@@ -111,7 +111,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: '14.x'
|
||||||
displayName: 'Install Node.js'
|
displayName: 'Install Node.js'
|
||||||
- script: yarn --frozen-lockfile
|
- script: yarn --frozen-lockfile
|
||||||
displayName: 'Install dependencies and build'
|
displayName: 'Install dependencies and build'
|
||||||
@@ -128,7 +128,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: '14.x'
|
||||||
displayName: 'Install Node.js'
|
displayName: 'Install Node.js'
|
||||||
- script: yarn --frozen-lockfile
|
- script: yarn --frozen-lockfile
|
||||||
displayName: 'Install dependencies and build'
|
displayName: 'Install dependencies and build'
|
||||||
@@ -151,7 +151,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: '14.x'
|
||||||
displayName: 'Install Node.js'
|
displayName: 'Install Node.js'
|
||||||
- task: YarnInstaller@3
|
- task: YarnInstaller@3
|
||||||
inputs:
|
inputs:
|
||||||
|
|||||||
+24
-24
@@ -34,37 +34,37 @@
|
|||||||
"vtfeatures": "node bin/extract_vtfeatures.js src/**/*.ts src/*.ts"
|
"vtfeatures": "node bin/extract_vtfeatures.js src/**/*.ts src/*.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.2.14",
|
"@types/chai": "^4.2.18",
|
||||||
"@types/debug": "^4.1.5",
|
"@types/debug": "^4.1.5",
|
||||||
"@types/deep-equal": "^1.0.1",
|
"@types/deep-equal": "^1.0.1",
|
||||||
"@types/glob": "^7.1.3",
|
"@types/glob": "^7.1.3",
|
||||||
"@types/jsdom": "^16.2.5",
|
"@types/jsdom": "^16.2.10",
|
||||||
"@types/mocha": "^8.0.3",
|
"@types/mocha": "^8.2.2",
|
||||||
"@types/node": "^10.17.17",
|
"@types/node": "^14.14.44",
|
||||||
"@types/utf8": "^2.1.6",
|
"@types/utf8": "^2.1.6",
|
||||||
"@types/webpack": "^4.41.24",
|
"@types/webpack": "^5.28.0",
|
||||||
"@types/ws": "^7.2.9",
|
"@types/ws": "^7.4.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.0.0",
|
"@typescript-eslint/eslint-plugin": "^4.23.0",
|
||||||
"@typescript-eslint/parser": "^3.10.1",
|
"@typescript-eslint/parser": "^4.23.0",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.3.4",
|
||||||
"deep-equal": "^2.0.4",
|
"deep-equal": "^2.0.5",
|
||||||
"eslint": "^7.12.1",
|
"eslint": "^7.26.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-ws": "^4.0.0",
|
"express-ws": "^4.0.0",
|
||||||
"glob": "^7.0.5",
|
"glob": "^7.1.7",
|
||||||
"jsdom": "^16.4.0",
|
"jsdom": "^16.5.3",
|
||||||
"mocha": "^8.2.1",
|
"mocha": "^8.4.0",
|
||||||
"mustache": "^4.0.1",
|
"mustache": "^4.2.0",
|
||||||
"node-pty": "^0.9.0",
|
"node-pty": "^0.10.1",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"playwright": "^1.5.2",
|
"playwright": "^1.11.0",
|
||||||
"source-map-loader": "^1.1.2",
|
"source-map-loader": "^2.0.1",
|
||||||
"ts-loader": "^8.0.8",
|
"ts-loader": "^9.1.2",
|
||||||
"typescript": "4.0",
|
"typescript": "^4.2.4",
|
||||||
"utf8": "^3.0.0",
|
"utf8": "^3.0.0",
|
||||||
"webpack": "^5.4.0",
|
"webpack": "^5.37.0",
|
||||||
"webpack-cli": "^4.2.0",
|
"webpack-cli": "^4.7.0",
|
||||||
"ws": "^7.3.1",
|
"ws": "^7.4.5",
|
||||||
"xterm-benchmark": "^0.1.3"
|
"xterm-benchmark": "^0.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ export class AccessibilityManager extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _onBoundaryFocus(e: FocusEvent, position: BoundaryPosition): void {
|
private _onBoundaryFocus(e: FocusEvent, position: BoundaryPosition): void {
|
||||||
const boundaryElement = <HTMLElement>e.target;
|
const boundaryElement = e.target as HTMLElement;
|
||||||
const beforeBoundaryElement = this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2];
|
const beforeBoundaryElement = this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2];
|
||||||
|
|
||||||
// Don't scroll if the buffer top has reached the end in that direction
|
// Don't scroll if the buffer top has reached the end in that direction
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ describe('ColorManager', () => {
|
|||||||
dom = new jsdom.JSDOM('');
|
dom = new jsdom.JSDOM('');
|
||||||
window = dom.window;
|
window = dom.window;
|
||||||
document = window.document;
|
document = window.document;
|
||||||
(<any>window).HTMLCanvasElement.prototype.getContext = () => ({
|
(window as any).HTMLCanvasElement.prototype.getContext = () => ({
|
||||||
createLinearGradient(): any {
|
createLinearGradient(): any {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
@@ -36,7 +36,7 @@ describe('ColorManager', () => {
|
|||||||
for (const key of Object.keys(cm.colors)) {
|
for (const key of Object.keys(cm.colors)) {
|
||||||
if (key !== 'ansi' && key !== 'contrastCache') {
|
if (key !== 'ansi' && key !== 'contrastCache') {
|
||||||
// A #rrggbb or rgba(...)
|
// A #rrggbb or rgba(...)
|
||||||
assert.ok((<any>cm.colors)[key].css.length >= 7);
|
assert.ok((cm.colors as any)[key].css.length >= 7);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.equal(cm.colors.ansi.length, 256);
|
assert.equal(cm.colors.ansi.length, 256);
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ describe('Linkifier', () => {
|
|||||||
assert.equal(mouseZoneManager.zones[0].y1, 1);
|
assert.equal(mouseZoneManager.zones[0].y1, 1);
|
||||||
assert.equal(mouseZoneManager.zones[0].y2, 1);
|
assert.equal(mouseZoneManager.zones[0].y2, 1);
|
||||||
// Fires done()
|
// Fires done()
|
||||||
mouseZoneManager.zones[0].clickCallback(<any>{});
|
mouseZoneManager.zones[0].clickCallback({} as any);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
linkifier.linkifyRows();
|
linkifier.linkifyRows();
|
||||||
@@ -210,7 +210,7 @@ describe('Linkifier', () => {
|
|||||||
let count = 0;
|
let count = 0;
|
||||||
linkifier.registerLinkMatcher(/test/, () => assert.fail(), {
|
linkifier.registerLinkMatcher(/test/, () => assert.fail(), {
|
||||||
validationCallback: (url, cb) => {
|
validationCallback: (url, cb) => {
|
||||||
count += 1;
|
count++;
|
||||||
if (count === 2) {
|
if (count === 2) {
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,9 @@ export class Linkifier implements ILinkifier {
|
|||||||
if (this._rowsTimeoutId) {
|
if (this._rowsTimeoutId) {
|
||||||
clearTimeout(this._rowsTimeoutId);
|
clearTimeout(this._rowsTimeoutId);
|
||||||
}
|
}
|
||||||
this._rowsTimeoutId = <number><any>setTimeout(() => this._linkifyRows(), Linkifier._timeBeforeLatency);
|
|
||||||
|
// Cannot use window.setTimeout since tests need to run in node
|
||||||
|
this._rowsTimeoutId = setTimeout(() => this._linkifyRows(), Linkifier._timeBeforeLatency) as any as number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ describe('Terminal', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
term = new TestTerminal(termOptions);
|
term = new TestTerminal(termOptions);
|
||||||
term.refresh = () => { };
|
term.refresh = () => { };
|
||||||
(<any>term).renderer = new MockRenderer();
|
(term as any).renderer = new MockRenderer();
|
||||||
term.viewport = new MockViewport();
|
term.viewport = new MockViewport();
|
||||||
(<any>term)._compositionHelper = new MockCompositionHelper();
|
(term as any)._compositionHelper = new MockCompositionHelper();
|
||||||
(<any>term).element = {
|
(term as any).element = {
|
||||||
classList: {
|
classList: {
|
||||||
toggle: () => { },
|
toggle: () => { },
|
||||||
remove: () => { }
|
remove: () => { }
|
||||||
@@ -86,12 +86,12 @@ describe('Terminal', () => {
|
|||||||
assert.equal(e.domEvent instanceof Object, true);
|
assert.equal(e.domEvent instanceof Object, true);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
const evKeyPress = <KeyboardEvent>{
|
const evKeyPress = {
|
||||||
preventDefault: () => { },
|
preventDefault: () => { },
|
||||||
stopPropagation: () => { },
|
stopPropagation: () => { },
|
||||||
type: 'keypress',
|
type: 'keypress',
|
||||||
keyCode: 13
|
keyCode: 13
|
||||||
};
|
} as KeyboardEvent;
|
||||||
term.keyPress(evKeyPress);
|
term.keyPress(evKeyPress);
|
||||||
});
|
});
|
||||||
it('should fire a key event after a keydown DOM event', (done) => {
|
it('should fire a key event after a keydown DOM event', (done) => {
|
||||||
@@ -100,13 +100,13 @@ describe('Terminal', () => {
|
|||||||
assert.equal(e.domEvent instanceof Object, true);
|
assert.equal(e.domEvent instanceof Object, true);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
(<any>term).textarea = { value: '' };
|
(term as any).textarea = { value: '' };
|
||||||
const evKeyDown = <KeyboardEvent>{
|
const evKeyDown = {
|
||||||
preventDefault: () => { },
|
preventDefault: () => { },
|
||||||
stopPropagation: () => { },
|
stopPropagation: () => { },
|
||||||
type: 'keydown',
|
type: 'keydown',
|
||||||
keyCode: 13
|
keyCode: 13
|
||||||
};
|
} as KeyboardEvent;
|
||||||
term.keyDown(evKeyDown);
|
term.keyDown(evKeyDown);
|
||||||
});
|
});
|
||||||
it('should fire the onResize event', (done) => {
|
it('should fire the onResize event', (done) => {
|
||||||
@@ -140,18 +140,18 @@ describe('Terminal', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('attachCustomKeyEventHandler', () => {
|
describe('attachCustomKeyEventHandler', () => {
|
||||||
const evKeyDown = <KeyboardEvent>{
|
const evKeyDown = {
|
||||||
preventDefault: () => { },
|
preventDefault: () => { },
|
||||||
stopPropagation: () => { },
|
stopPropagation: () => { },
|
||||||
type: 'keydown',
|
type: 'keydown',
|
||||||
keyCode: 77
|
keyCode: 77
|
||||||
};
|
} as KeyboardEvent;
|
||||||
const evKeyPress = <KeyboardEvent>{
|
const evKeyPress = {
|
||||||
preventDefault: () => { },
|
preventDefault: () => { },
|
||||||
stopPropagation: () => { },
|
stopPropagation: () => { },
|
||||||
type: 'keypress',
|
type: 'keypress',
|
||||||
keyCode: 77
|
keyCode: 77
|
||||||
};
|
} as KeyboardEvent;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
term.clearSelection = () => { };
|
term.clearSelection = () => { };
|
||||||
@@ -374,13 +374,13 @@ describe('Terminal', () => {
|
|||||||
|
|
||||||
describe('keyPress', () => {
|
describe('keyPress', () => {
|
||||||
it('should scroll down, when a key is pressed and terminal is scrolled up', () => {
|
it('should scroll down, when a key is pressed and terminal is scrolled up', () => {
|
||||||
const event = <KeyboardEvent>{
|
const event = {
|
||||||
type: 'keydown',
|
type: 'keydown',
|
||||||
key: 'a',
|
key: 'a',
|
||||||
keyCode: 65,
|
keyCode: 65,
|
||||||
preventDefault: () => { },
|
preventDefault: () => { },
|
||||||
stopPropagation: () => { }
|
stopPropagation: () => { }
|
||||||
};
|
} as KeyboardEvent;
|
||||||
|
|
||||||
term.buffer.ydisp = 0;
|
term.buffer.ydisp = 0;
|
||||||
term.buffer.ybase = 40;
|
term.buffer.ybase = 40;
|
||||||
@@ -403,7 +403,7 @@ describe('Terminal', () => {
|
|||||||
assert.equal(term.buffer.ydisp, startYDisp);
|
assert.equal(term.buffer.ydisp, startYDisp);
|
||||||
term.scrollLines(-1);
|
term.scrollLines(-1);
|
||||||
assert.equal(term.buffer.ydisp, startYDisp - 1);
|
assert.equal(term.buffer.ydisp, startYDisp - 1);
|
||||||
term.keyPress(<KeyboardEvent>{ keyCode: 0 });
|
term.keyPress({ keyCode: 0 });
|
||||||
assert.equal(term.buffer.ydisp, startYDisp - 1);
|
assert.equal(term.buffer.ydisp, startYDisp - 1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|||||||
|
|
||||||
// private _visualBellTimer: number;
|
// private _visualBellTimer: number;
|
||||||
|
|
||||||
public browser: IBrowser = <any>Browser;
|
public browser: IBrowser = Browser as any;
|
||||||
|
|
||||||
// TODO: We should remove options once components adopt optionsService
|
// TODO: We should remove options once components adopt optionsService
|
||||||
public get options(): IInitializedTerminalOptions { return this.optionsService.options; }
|
public get options(): IInitializedTerminalOptions { return this.optionsService.options; }
|
||||||
@@ -601,7 +601,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
|||||||
|
|
||||||
let but: CoreMouseButton;
|
let but: CoreMouseButton;
|
||||||
let action: CoreMouseAction | undefined;
|
let action: CoreMouseAction | undefined;
|
||||||
switch ((<any>ev).overrideType || ev.type) {
|
switch ((ev as any).overrideType || ev.type) {
|
||||||
case 'mousemove':
|
case 'mousemove':
|
||||||
action = CoreMouseAction.MOVE;
|
action = CoreMouseAction.MOVE;
|
||||||
if (ev.buttons === undefined) {
|
if (ev.buttons === undefined) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const ROWS = 25;
|
|||||||
|
|
||||||
const TESTFILES = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '../..')});
|
const TESTFILES = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '../..')});
|
||||||
const SKIP_FILES = [
|
const SKIP_FILES = [
|
||||||
|
't0055-EL.in', // EL/ED handle cursor at cols differently (see #3362)
|
||||||
't0084-CBT.in',
|
't0084-CBT.in',
|
||||||
't0101-NLM.in',
|
't0101-NLM.in',
|
||||||
't0103-reverse_wrap.in', // not comparable, we deviate from xterm reverse wrap on purpose
|
't0103-reverse_wrap.in', // not comparable, we deviate from xterm reverse wrap on purpose
|
||||||
@@ -73,7 +74,7 @@ describe('Escape Sequence Files', function(): void {
|
|||||||
// register handler to trigger viewport scraping, wait for it to finish
|
// register handler to trigger viewport scraping, wait for it to finish
|
||||||
let content = '';
|
let content = '';
|
||||||
const OSC_CODE = 12345;
|
const OSC_CODE = 12345;
|
||||||
await new Promise(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
customHandler = term.registerOscHandler(OSC_CODE, () => {
|
customHandler = term.registerOscHandler(OSC_CODE, () => {
|
||||||
// grab terminal viewport content
|
// grab terminal viewport content
|
||||||
content = terminalToString(term);
|
content = terminalToString(term);
|
||||||
@@ -105,7 +106,7 @@ function formatError(input: string, output: string, expected: string): string {
|
|||||||
function addLineNumber(start: number, color: string): (s: string) => string {
|
function addLineNumber(start: number, color: string): (s: string) => string {
|
||||||
let counter = start || 0;
|
let counter = start || 0;
|
||||||
return (s: string): string => {
|
return (s: string): string => {
|
||||||
counter += 1;
|
counter++;
|
||||||
return '\x1b[33m' + (' ' + counter).slice(-2) + color + s;
|
return '\x1b[33m' + (' ' + counter).slice(-2) + color + s;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export class MockTerminal implements ITerminal {
|
|||||||
public textarea!: HTMLTextAreaElement;
|
public textarea!: HTMLTextAreaElement;
|
||||||
public rows!: number;
|
public rows!: number;
|
||||||
public cols!: number;
|
public cols!: number;
|
||||||
public browser: IBrowser = <any>Browser;
|
public browser: IBrowser = Browser as any;
|
||||||
public writeBuffer!: string[];
|
public writeBuffer!: string[];
|
||||||
public children!: HTMLElement[];
|
public children!: HTMLElement[];
|
||||||
public cursorHidden!: boolean;
|
public cursorHidden!: boolean;
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ describe('CompositionHelper', () => {
|
|||||||
it('Should insert simple characters', (done) => {
|
it('Should insert simple characters', (done) => {
|
||||||
// First character 'ㅇ'
|
// First character 'ㅇ'
|
||||||
compositionHelper.compositionstart();
|
compositionHelper.compositionstart();
|
||||||
compositionHelper.compositionupdate(<CompositionEvent><CompositionEvent>{ data: 'ㅇ' });
|
compositionHelper.compositionupdate({ data: 'ㅇ' });
|
||||||
textarea.value = 'ㅇ';
|
textarea.value = 'ㅇ';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionend();
|
compositionHelper.compositionend();
|
||||||
@@ -57,7 +57,7 @@ describe('CompositionHelper', () => {
|
|||||||
assert.equal(handledText, 'ㅇ');
|
assert.equal(handledText, 'ㅇ');
|
||||||
// Second character 'ㅇ'
|
// Second character 'ㅇ'
|
||||||
compositionHelper.compositionstart();
|
compositionHelper.compositionstart();
|
||||||
compositionHelper.compositionupdate(<CompositionEvent><CompositionEvent>{ data: 'ㅇ' });
|
compositionHelper.compositionupdate({ data: 'ㅇ' });
|
||||||
textarea.value = 'ㅇㅇ';
|
textarea.value = 'ㅇㅇ';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionend();
|
compositionHelper.compositionend();
|
||||||
@@ -73,13 +73,13 @@ describe('CompositionHelper', () => {
|
|||||||
it('Should insert complex characters', (done) => {
|
it('Should insert complex characters', (done) => {
|
||||||
// First character '앙'
|
// First character '앙'
|
||||||
compositionHelper.compositionstart();
|
compositionHelper.compositionstart();
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'ㅇ' });
|
compositionHelper.compositionupdate({ data: 'ㅇ' });
|
||||||
textarea.value = 'ㅇ';
|
textarea.value = 'ㅇ';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: '아' });
|
compositionHelper.compositionupdate({ data: '아' });
|
||||||
textarea.value = '아';
|
textarea.value = '아';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: '앙' });
|
compositionHelper.compositionupdate({ data: '앙' });
|
||||||
textarea.value = '앙';
|
textarea.value = '앙';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionend();
|
compositionHelper.compositionend();
|
||||||
@@ -87,13 +87,13 @@ describe('CompositionHelper', () => {
|
|||||||
assert.equal(handledText, '앙');
|
assert.equal(handledText, '앙');
|
||||||
// Second character '앙'
|
// Second character '앙'
|
||||||
compositionHelper.compositionstart();
|
compositionHelper.compositionstart();
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'ㅇ' });
|
compositionHelper.compositionupdate({ data: 'ㅇ' });
|
||||||
textarea.value = '앙ㅇ';
|
textarea.value = '앙ㅇ';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: '아' });
|
compositionHelper.compositionupdate({ data: '아' });
|
||||||
textarea.value = '앙아';
|
textarea.value = '앙아';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: '앙' });
|
compositionHelper.compositionupdate({ data: '앙' });
|
||||||
textarea.value = '앙앙';
|
textarea.value = '앙앙';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionend();
|
compositionHelper.compositionend();
|
||||||
@@ -113,19 +113,19 @@ describe('CompositionHelper', () => {
|
|||||||
it('Should insert complex characters that change with following character', (done) => {
|
it('Should insert complex characters that change with following character', (done) => {
|
||||||
// First character '아'
|
// First character '아'
|
||||||
compositionHelper.compositionstart();
|
compositionHelper.compositionstart();
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'ㅇ' });
|
compositionHelper.compositionupdate({ data: 'ㅇ' });
|
||||||
textarea.value = 'ㅇ';
|
textarea.value = 'ㅇ';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: '아' });
|
compositionHelper.compositionupdate({ data: '아' });
|
||||||
textarea.value = '아';
|
textarea.value = '아';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
// Start second character '아' in first character
|
// Start second character '아' in first character
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: '앙' });
|
compositionHelper.compositionupdate({ data: '앙' });
|
||||||
textarea.value = '앙';
|
textarea.value = '앙';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionend();
|
compositionHelper.compositionend();
|
||||||
compositionHelper.compositionstart();
|
compositionHelper.compositionstart();
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: '아' });
|
compositionHelper.compositionupdate({ data: '아' });
|
||||||
textarea.value = '아아';
|
textarea.value = '아아';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionend();
|
compositionHelper.compositionend();
|
||||||
@@ -142,14 +142,14 @@ describe('CompositionHelper', () => {
|
|||||||
it('Should insert multi-characters compositions', (done) => {
|
it('Should insert multi-characters compositions', (done) => {
|
||||||
// First character 'だ'
|
// First character 'だ'
|
||||||
compositionHelper.compositionstart();
|
compositionHelper.compositionstart();
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'd' });
|
compositionHelper.compositionupdate({ data: 'd' });
|
||||||
textarea.value = 'd';
|
textarea.value = 'd';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'だ' });
|
compositionHelper.compositionupdate({ data: 'だ' });
|
||||||
textarea.value = 'だ';
|
textarea.value = 'だ';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
// Second character 'あ'
|
// Second character 'あ'
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'だあ' });
|
compositionHelper.compositionupdate({ data: 'だあ' });
|
||||||
textarea.value = 'だあ';
|
textarea.value = 'だあ';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionend();
|
compositionHelper.compositionend();
|
||||||
@@ -165,18 +165,18 @@ describe('CompositionHelper', () => {
|
|||||||
it('Should insert multi-character compositions that are converted to other characters with the same length', (done) => {
|
it('Should insert multi-character compositions that are converted to other characters with the same length', (done) => {
|
||||||
// First character 'だ'
|
// First character 'だ'
|
||||||
compositionHelper.compositionstart();
|
compositionHelper.compositionstart();
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'd' });
|
compositionHelper.compositionupdate({ data: 'd' });
|
||||||
textarea.value = 'd';
|
textarea.value = 'd';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'だ' });
|
compositionHelper.compositionupdate({ data: 'だ' });
|
||||||
textarea.value = 'だ';
|
textarea.value = 'だ';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
// Second character 'ー'
|
// Second character 'ー'
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'だー' });
|
compositionHelper.compositionupdate({ data: 'だー' });
|
||||||
textarea.value = 'だー';
|
textarea.value = 'だー';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
// Convert to katakana 'ダー'
|
// Convert to katakana 'ダー'
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'ダー' });
|
compositionHelper.compositionupdate({ data: 'ダー' });
|
||||||
textarea.value = 'ダー';
|
textarea.value = 'ダー';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionend();
|
compositionHelper.compositionend();
|
||||||
@@ -193,18 +193,18 @@ describe('CompositionHelper', () => {
|
|||||||
it('Should insert multi-character compositions that are converted to other characters with different lengths', (done) => {
|
it('Should insert multi-character compositions that are converted to other characters with different lengths', (done) => {
|
||||||
// First character 'い'
|
// First character 'い'
|
||||||
compositionHelper.compositionstart();
|
compositionHelper.compositionstart();
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'い' });
|
compositionHelper.compositionupdate({ data: 'い' });
|
||||||
textarea.value = 'い';
|
textarea.value = 'い';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
// Second character 'ま'
|
// Second character 'ま'
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'いm' });
|
compositionHelper.compositionupdate({ data: 'いm' });
|
||||||
textarea.value = 'いm';
|
textarea.value = 'いm';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'いま' });
|
compositionHelper.compositionupdate({ data: 'いま' });
|
||||||
textarea.value = 'いま';
|
textarea.value = 'いま';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
// Convert to kanji '今'
|
// Convert to kanji '今'
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: '今' });
|
compositionHelper.compositionupdate({ data: '今' });
|
||||||
textarea.value = '今';
|
textarea.value = '今';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionend();
|
compositionHelper.compositionend();
|
||||||
@@ -221,7 +221,7 @@ describe('CompositionHelper', () => {
|
|||||||
it('Should insert non-composition characters input immediately after composition characters', (done) => {
|
it('Should insert non-composition characters input immediately after composition characters', (done) => {
|
||||||
// First character 'ㅇ'
|
// First character 'ㅇ'
|
||||||
compositionHelper.compositionstart();
|
compositionHelper.compositionstart();
|
||||||
compositionHelper.compositionupdate(<CompositionEvent>{ data: 'ㅇ' });
|
compositionHelper.compositionupdate({ data: 'ㅇ' });
|
||||||
textarea.value = 'ㅇ';
|
textarea.value = 'ㅇ';
|
||||||
setTimeout(() => { // wait for any textarea updates
|
setTimeout(() => { // wait for any textarea updates
|
||||||
compositionHelper.compositionend();
|
compositionHelper.compositionend();
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export class CompositionHelper {
|
|||||||
* Handles the compositionupdate event, updating the composition view.
|
* Handles the compositionupdate event, updating the composition view.
|
||||||
* @param ev The event.
|
* @param ev The event.
|
||||||
*/
|
*/
|
||||||
public compositionupdate(ev: CompositionEvent): void {
|
public compositionupdate(ev: Pick<CompositionEvent, 'data'>): void {
|
||||||
this._compositionView.textContent = ev.data;
|
this._compositionView.textContent = ev.data;
|
||||||
this.updateCompositionElements();
|
this.updateCompositionElements();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export class AddonManager implements IDisposable {
|
|||||||
};
|
};
|
||||||
this._addons.push(loadedAddon);
|
this._addons.push(loadedAddon);
|
||||||
instance.dispose = () => this._wrappedAddonDispose(loadedAddon);
|
instance.dispose = () => this._wrappedAddonDispose(loadedAddon);
|
||||||
instance.activate(<any>terminal);
|
instance.activate(terminal as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _wrappedAddonDispose(loadedAddon: ILoadedAddon): void {
|
private _wrappedAddonDispose(loadedAddon: ILoadedAddon): void {
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ class BufferLineApiView implements IBufferLineApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cell) {
|
if (cell) {
|
||||||
this._line.loadCell(x, <ICellData>cell);
|
this._line.loadCell(x, cell as ICellData);
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
return this._line.loadCell(x, new CellData());
|
return this._line.loadCell(x, new CellData());
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { ITerminalOptions } from 'common/services/Services';
|
|||||||
|
|
||||||
export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, options: ITerminalOptions, colors: IColorSet): ICharAtlasConfig {
|
export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, options: ITerminalOptions, colors: IColorSet): ICharAtlasConfig {
|
||||||
// null out some fields that don't matter
|
// null out some fields that don't matter
|
||||||
const clonedColors = <IPartialColorSet>{
|
const clonedColors: IPartialColorSet = {
|
||||||
foreground: colors.foreground,
|
foreground: colors.foreground,
|
||||||
background: colors.background,
|
background: colors.background,
|
||||||
cursor: undefined,
|
cursor: undefined,
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
|||||||
if (!row) {
|
if (!row) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const span = <HTMLElement>row.children[x];
|
const span = row.children[x] as HTMLElement;
|
||||||
if (span) {
|
if (span) {
|
||||||
span.style.textDecoration = enabled ? 'underline' : 'none';
|
span.style.textDecoration = enabled ? 'underline' : 'none';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ function lineData(data: IPartialLineData[]): IBufferLine {
|
|||||||
const tline = new BufferLine(0);
|
const tline = new BufferLine(0);
|
||||||
for (let i = 0; i < data.length; ++i) {
|
for (let i = 0; i < data.length; ++i) {
|
||||||
const line = data[i][0];
|
const line = data[i][0];
|
||||||
const attr = <number>(data[i][1] || 0);
|
const attr = (data[i][1] || 0) as number;
|
||||||
const offset = tline.length;
|
const offset = tline.length;
|
||||||
tline.resize(tline.length + line.split('').length, CellData.fromCharData([0, '', 0, 0]));
|
tline.resize(tline.length + line.split('').length, CellData.fromCharData([0, '', 0, 0]));
|
||||||
line.split('').map((char, idx) => tline.setCell(idx + offset, CellData.fromCharData([attr, char, 1, char.charCodeAt(0)])));
|
line.split('').map((char, idx) => tline.setCell(idx + offset, CellData.fromCharData([attr, char, 1, char.charCodeAt(0)])));
|
||||||
|
|||||||
@@ -133,8 +133,8 @@ export class SelectionService extends Disposable implements ISelectionService {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
// Init listeners
|
// Init listeners
|
||||||
this._mouseMoveListener = event => this._onMouseMove(<MouseEvent>event);
|
this._mouseMoveListener = event => this._onMouseMove(event as MouseEvent);
|
||||||
this._mouseUpListener = event => this._onMouseUp(<MouseEvent>event);
|
this._mouseUpListener = event => this._onMouseUp(event as MouseEvent);
|
||||||
this._coreService.onUserInput(() => {
|
this._coreService.onUserInput(() => {
|
||||||
if (this.hasSelection) {
|
if (this.hasSelection) {
|
||||||
this.clearSelection();
|
this.clearSelection();
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export class SoundService implements ISoundService {
|
|||||||
|
|
||||||
public static get audioContext(): AudioContext | null {
|
public static get audioContext(): AudioContext | null {
|
||||||
if (!SoundService._audioContext) {
|
if (!SoundService._audioContext) {
|
||||||
const audioContextCtor: typeof AudioContext = (<any>window).AudioContext || (<any>window).webkitAudioContext;
|
const audioContextCtor: typeof AudioContext = (window as any).AudioContext || (window as any).webkitAudioContext;
|
||||||
if (!audioContextCtor) {
|
if (!audioContextCtor) {
|
||||||
console.warn('Web Audio API is not supported by this browser. Consider upgrading to the latest version');
|
console.warn('Web Audio API is not supported by this browser. Consider upgrading to the latest version');
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ export function clone<T>(val: T, depth: number = 5): T {
|
|||||||
|
|
||||||
for (const key in val) {
|
for (const key in val) {
|
||||||
// Recursively clone eack item unless we're at the maximum depth
|
// Recursively clone eack item unless we're at the maximum depth
|
||||||
clonedObject[key] = depth <= 1 ? val[key] : (val[key] ? clone(val[key], depth - 1) : val[key]);
|
clonedObject[key] = depth <= 1 ? val[key] : (val[key] && clone(val[key], depth - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
return clonedObject as T;
|
return clonedObject as T;
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ describe('InputHandler', () => {
|
|||||||
optionsService.options.scrollback = 1;
|
optionsService.options.scrollback = 1;
|
||||||
bufferService.reset();
|
bufferService.reset();
|
||||||
});
|
});
|
||||||
it('SL (scrollLeft)', async () => {
|
it('SL (scrollLeft)', () => {
|
||||||
inputHandler.parseP('12345'.repeat(6));
|
inputHandler.parseP('12345'.repeat(6));
|
||||||
inputHandler.parseP('\x1b[ @');
|
inputHandler.parseP('\x1b[ @');
|
||||||
assert.deepEqual(getLines(bufferService, 6), ['12345', '2345', '2345', '2345', '2345', '2345']);
|
assert.deepEqual(getLines(bufferService, 6), ['12345', '2345', '2345', '2345', '2345', '2345']);
|
||||||
@@ -86,7 +86,7 @@ describe('InputHandler', () => {
|
|||||||
inputHandler.parseP('\x1b[2 @');
|
inputHandler.parseP('\x1b[2 @');
|
||||||
assert.deepEqual(getLines(bufferService, 6), ['12345', '5', '5', '5', '5', '5']);
|
assert.deepEqual(getLines(bufferService, 6), ['12345', '5', '5', '5', '5', '5']);
|
||||||
});
|
});
|
||||||
it('SR (scrollRight)', async () => {
|
it('SR (scrollRight)', () => {
|
||||||
inputHandler.parseP('12345'.repeat(6));
|
inputHandler.parseP('12345'.repeat(6));
|
||||||
inputHandler.parseP('\x1b[ A');
|
inputHandler.parseP('\x1b[ A');
|
||||||
assert.deepEqual(getLines(bufferService, 6), ['12345', ' 1234', ' 1234', ' 1234', ' 1234', ' 1234']);
|
assert.deepEqual(getLines(bufferService, 6), ['12345', ' 1234', ' 1234', ' 1234', ' 1234', ' 1234']);
|
||||||
@@ -95,7 +95,7 @@ describe('InputHandler', () => {
|
|||||||
inputHandler.parseP('\x1b[2 A');
|
inputHandler.parseP('\x1b[2 A');
|
||||||
assert.deepEqual(getLines(bufferService, 6), ['12345', ' 1', ' 1', ' 1', ' 1', ' 1']);
|
assert.deepEqual(getLines(bufferService, 6), ['12345', ' 1', ' 1', ' 1', ' 1', ' 1']);
|
||||||
});
|
});
|
||||||
it('insertColumns (DECIC)', async () => {
|
it('insertColumns (DECIC)', () => {
|
||||||
inputHandler.parseP('12345'.repeat(6));
|
inputHandler.parseP('12345'.repeat(6));
|
||||||
inputHandler.parseP('\x1b[3;3H');
|
inputHandler.parseP('\x1b[3;3H');
|
||||||
inputHandler.parseP('\x1b[\'}');
|
inputHandler.parseP('\x1b[\'}');
|
||||||
@@ -111,7 +111,7 @@ describe('InputHandler', () => {
|
|||||||
inputHandler.parseP('\x1b[2\'}');
|
inputHandler.parseP('\x1b[2\'}');
|
||||||
assert.deepEqual(getLines(bufferService, 6), ['12345', '12 3', '12 3', '12 3', '12 3', '12 3']);
|
assert.deepEqual(getLines(bufferService, 6), ['12345', '12 3', '12 3', '12 3', '12 3', '12 3']);
|
||||||
});
|
});
|
||||||
it('deleteColumns (DECDC)', async () => {
|
it('deleteColumns (DECDC)', () => {
|
||||||
inputHandler.parseP('12345'.repeat(6));
|
inputHandler.parseP('12345'.repeat(6));
|
||||||
inputHandler.parseP('\x1b[3;3H');
|
inputHandler.parseP('\x1b[3;3H');
|
||||||
inputHandler.parseP('\x1b[\'~');
|
inputHandler.parseP('\x1b[\'~');
|
||||||
@@ -137,7 +137,7 @@ describe('InputHandler', () => {
|
|||||||
bufferService.reset();
|
bufferService.reset();
|
||||||
});
|
});
|
||||||
describe('reverseWraparound set', () => {
|
describe('reverseWraparound set', () => {
|
||||||
it('should not reverse outside of scroll margins', async () => {
|
it('should not reverse outside of scroll margins', () => {
|
||||||
// prepare buffer content
|
// prepare buffer content
|
||||||
inputHandler.parseP('#####abcdefghijklmnopqrstuvwxy');
|
inputHandler.parseP('#####abcdefghijklmnopqrstuvwxy');
|
||||||
assert.deepEqual(getLines(bufferService, 6), ['#####', 'abcde', 'fghij', 'klmno', 'pqrst', 'uvwxy']);
|
assert.deepEqual(getLines(bufferService, 6), ['#####', 'abcde', 'fghij', 'klmno', 'pqrst', 'uvwxy']);
|
||||||
@@ -1913,6 +1913,92 @@ describe('InputHandler', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// issue #3362 and #2979
|
||||||
|
describe('EL/ED cursor at buffer.cols', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
bufferService.resize(10, 5);
|
||||||
|
});
|
||||||
|
describe('cursor should stay at cols / does not overflow', () => {
|
||||||
|
it('EL0', async () => {
|
||||||
|
await inputHandler.parseP('##########\x1b[0K');
|
||||||
|
assert.equal(bufferService.buffer.x, 10);
|
||||||
|
assert.deepEqual(getLines(bufferService), ['#'.repeat(10), '', '', '', '']);
|
||||||
|
});
|
||||||
|
it('EL1', async () => {
|
||||||
|
await inputHandler.parseP('##########\x1b[1K');
|
||||||
|
assert.equal(bufferService.buffer.x, 10);
|
||||||
|
assert.deepEqual(getLines(bufferService), ['', '', '', '', '']);
|
||||||
|
});
|
||||||
|
it('EL2', async () => {
|
||||||
|
await inputHandler.parseP('##########\x1b[2K');
|
||||||
|
assert.equal(bufferService.buffer.x, 10);
|
||||||
|
assert.deepEqual(getLines(bufferService), ['', '', '', '', '']);
|
||||||
|
});
|
||||||
|
it('ED0', async () => {
|
||||||
|
await inputHandler.parseP('##########\x1b[0J');
|
||||||
|
assert.equal(bufferService.buffer.x, 10);
|
||||||
|
assert.deepEqual(getLines(bufferService), ['#'.repeat(10), '', '', '', '']);
|
||||||
|
});
|
||||||
|
it('ED1', async () => {
|
||||||
|
await inputHandler.parseP('##########\x1b[1J');
|
||||||
|
assert.equal(bufferService.buffer.x, 10);
|
||||||
|
assert.deepEqual(getLines(bufferService), ['', '', '', '', '']);
|
||||||
|
});
|
||||||
|
it('ED2', async () => {
|
||||||
|
await inputHandler.parseP('##########\x1b[2J');
|
||||||
|
assert.equal(bufferService.buffer.x, 10);
|
||||||
|
assert.deepEqual(getLines(bufferService), ['', '', '', '', '']);
|
||||||
|
});
|
||||||
|
it('ED3', async () => {
|
||||||
|
await inputHandler.parseP('##########\x1b[3J');
|
||||||
|
assert.equal(bufferService.buffer.x, 10);
|
||||||
|
assert.deepEqual(getLines(bufferService), ['#'.repeat(10), '', '', '', '']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('following sequence keeps working', () => {
|
||||||
|
// sequences to test (cursor related ones)
|
||||||
|
const SEQ = [
|
||||||
|
/* ICH */ '\x1b[10@',
|
||||||
|
/* SL */ '\x1b[10 @',
|
||||||
|
/* CUU */ '\x1b[10A',
|
||||||
|
/* SR */ '\x1b[10 A',
|
||||||
|
/* CUD */ '\x1b[10B',
|
||||||
|
/* CUF */ '\x1b[10C',
|
||||||
|
/* CUB */ '\x1b[10D',
|
||||||
|
/* CNL */ '\x1b[10E',
|
||||||
|
/* CPL */ '\x1b[10F',
|
||||||
|
/* CHA */ '\x1b[10G',
|
||||||
|
/* CUP */ '\x1b[10;10H',
|
||||||
|
/* CHT */ '\x1b[10I',
|
||||||
|
/* IL */ '\x1b[10L',
|
||||||
|
/* DL */ '\x1b[10M',
|
||||||
|
/* DCH */ '\x1b[10P',
|
||||||
|
/* SU */ '\x1b[10S',
|
||||||
|
/* SD */ '\x1b[10T',
|
||||||
|
/* ECH */ '\x1b[10X',
|
||||||
|
/* CBT */ '\x1b[10Z',
|
||||||
|
/* HPA */ '\x1b[10`',
|
||||||
|
/* HPR */ '\x1b[10a',
|
||||||
|
/* REP */ '\x1b[10b',
|
||||||
|
/* VPA */ '\x1b[10d',
|
||||||
|
/* VPR */ '\x1b[10e',
|
||||||
|
/* HVP */ '\x1b[10;10f',
|
||||||
|
/* TBC */ '\x1b[0g',
|
||||||
|
/* SCOSC */ '\x1b[s',
|
||||||
|
/* DECIC */ '\x1b[10\'}',
|
||||||
|
/* DECDC */ '\x1b[10\'~'
|
||||||
|
];
|
||||||
|
it('cursor never advances beyond cols', async () => {
|
||||||
|
for (const seq of SEQ) {
|
||||||
|
await inputHandler.parseP('##########\x1b[2J' + seq);
|
||||||
|
assert.equal(bufferService.buffer.x <= bufferService.cols, true);
|
||||||
|
inputHandler.reset();
|
||||||
|
bufferService.reset();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1247,7 +1247,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
* @vt: #P[Protection attributes are not supported.] CSI DECSED "Selective Erase In Display" "CSI ? Ps J" "Currently the same as ED."
|
* @vt: #P[Protection attributes are not supported.] CSI DECSED "Selective Erase In Display" "CSI ? Ps J" "Currently the same as ED."
|
||||||
*/
|
*/
|
||||||
public eraseInDisplay(params: IParams): boolean {
|
public eraseInDisplay(params: IParams): boolean {
|
||||||
this._restrictCursor();
|
this._restrictCursor(this._bufferService.cols);
|
||||||
let j;
|
let j;
|
||||||
switch (params.params[0]) {
|
switch (params.params[0]) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -1319,7 +1319,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
|||||||
* @vt: #P[Protection attributes are not supported.] CSI DECSEL "Selective Erase In Line" "CSI ? Ps K" "Currently the same as EL."
|
* @vt: #P[Protection attributes are not supported.] CSI DECSEL "Selective Erase In Line" "CSI ? Ps K" "Currently the same as EL."
|
||||||
*/
|
*/
|
||||||
public eraseInLine(params: IParams): boolean {
|
public eraseInLine(params: IParams): boolean {
|
||||||
this._restrictCursor();
|
this._restrictCursor(this._bufferService.cols);
|
||||||
switch (params.params[0]) {
|
switch (params.params[0]) {
|
||||||
case 0:
|
case 0:
|
||||||
this._eraseInBufferLine(this._bufferService.buffer.y, this._bufferService.buffer.x, this._bufferService.cols);
|
this._eraseInBufferLine(this._bufferService.buffer.y, this._bufferService.buffer.x, this._bufferService.cols);
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export class MockOptionsService implements IOptionsService {
|
|||||||
constructor(testOptions?: IPartialTerminalOptions) {
|
constructor(testOptions?: IPartialTerminalOptions) {
|
||||||
if (testOptions) {
|
if (testOptions) {
|
||||||
for (const key of Object.keys(testOptions)) {
|
for (const key of Object.keys(testOptions)) {
|
||||||
this.options[key] = (<any>testOptions)[key];
|
this.options[key] = (testOptions as any)[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ describe('BufferRange', () => {
|
|||||||
assert.throws(() => getRangeLength(createRange(1, 3, 1, 1), 0));
|
assert.throws(() => getRangeLength(createRange(1, 3, 1, 1), 0));
|
||||||
});
|
});
|
||||||
it('should get range multiple lines', () => {
|
it('should get range multiple lines', () => {
|
||||||
assert.equal(getRangeLength(createRange(1, 1, 4, 5), 5), 23);
|
assert.equal(getRangeLength(createRange(1, 1, 4, 5), 5), 24);
|
||||||
});
|
});
|
||||||
it('should get range for end line right after start line', () => {
|
it('should get range for end line right after start line', () => {
|
||||||
assert.equal(getRangeLength(createRange(1, 1, 7, 2), 5), 11);
|
assert.equal(getRangeLength(createRange(1, 1, 7, 2), 5), 12);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,12 +5,9 @@
|
|||||||
|
|
||||||
import { IBufferRange } from 'xterm';
|
import { IBufferRange } from 'xterm';
|
||||||
|
|
||||||
export function getRangeLength(range: IBufferRange, cols: number): number {
|
export function getRangeLength(range: IBufferRange, bufferCols: number): number {
|
||||||
if (range.start.y === range.end.y) {
|
|
||||||
return range.end.x - range.start.x + 1;
|
|
||||||
}
|
|
||||||
if (range.start.y > range.end.y) {
|
if (range.start.y > range.end.y) {
|
||||||
throw new Error(`Buffer range end (${range.end.x}, ${range.end.y}) cannot be before start (${range.start.x}, ${range.start.y})`);
|
throw new Error(`Buffer range end (${range.end.x}, ${range.end.y}) cannot be before start (${range.start.x}, ${range.start.y})`);
|
||||||
}
|
}
|
||||||
return cols * (range.end.y - range.start.y - 1) + cols - range.start.x + range.end.x;
|
return bufferCols * (range.end.y - range.start.y) + (range.end.x - range.start.x + 1);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user