Compare commits

..
5 Commits
Author SHA1 Message Date
Daniel Imms ebb21710c5 Merge pull request #2179 from Tyriar/release/3.14_2174
Fix NPE in open
2019-06-03 12:34:14 -07:00
Daniel Imms 367e23b64e v3.14.2 2019-06-03 12:29:25 -07:00
Daniel Imms 322ae131a1 Fix NPE in open
Fixes #2174
2019-06-03 12:28:44 -07:00
Daniel Imms 4f35c21355 v3.14.1 2019-05-31 09:30:49 -07:00
Daniel Imms 13133d699f Make sure xterm.css gets built to dist/ 2019-05-31 09:30:40 -07:00
385 changed files with 23963 additions and 41151 deletions
+2 -9
View File
@@ -8,19 +8,12 @@ RUN apt-get update \
# Verify git and process tools are installed
RUN apt-get install -y git procps
# Install yarn, puppeteer deps
# Install yarn
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 \
&& 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 -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
&& apt-get -y install --no-install-recommends yarn
# Clean up
RUN apt-get autoremove -y \
+2 -2
View File
@@ -3,7 +3,7 @@
"dockerFile": "Dockerfile",
"appPort": 3000,
"extensions": [
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig"
"editorconfig.editorconfig",
"ms-vscode.vscode-typescript-tslint-plugin"
]
}
-162
View File
@@ -1,162 +0,0 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": [
"src/browser/tsconfig.json",
"src/common/tsconfig.json",
"test/api/tsconfig.json",
"test/benchmark/tsconfig.json",
"addons/xterm-addon-attach/src/tsconfig.json",
"addons/xterm-addon-fit/src/tsconfig.json",
"addons/xterm-addon-search/src/tsconfig.json",
"addons/xterm-addon-unicode11/src/tsconfig.json",
"addons/xterm-addon-web-links/src/tsconfig.json",
"addons/xterm-addon-webgl/src/tsconfig.json",
"addons/xterm-addon-serialize/src/tsconfig.json",
"addons/xterm-addon-serialize/benchmark/tsconfig.json",
"addons/xterm-addon-ligatures/src/tsconfig.json"
],
"sourceType": "module"
},
"ignorePatterns": [
"**/typings/*.d.ts",
"**/node_modules",
"**/*.js"
],
"plugins": [
"@typescript-eslint"
],
"rules": {
"no-extra-semi": "error",
"@typescript-eslint/array-type": [
"warn",
{
"default": "array-simple",
"readonly": "generic"
}
],
"@typescript-eslint/class-name-casing": "warn",
"@typescript-eslint/consistent-type-definitions": "warn",
"@typescript-eslint/explicit-function-return-type": [
"warn",
{
"allowExpressions": true
}
],
"@typescript-eslint/explicit-member-accessibility": [
"warn",
{
"accessibility": "explicit",
"overrides": {
"constructors": "off"
}
}
],
"@typescript-eslint/indent": [
"warn",
2
],
"@typescript-eslint/interface-name-prefix": [
"warn",
"always"
],
"@typescript-eslint/member-delimiter-style": [
"warn",
{
"multiline": {
"delimiter": "semi",
"requireLast": true
},
"singleline": {
"delimiter": "comma",
"requireLast": false
}
}
],
"@typescript-eslint/naming-convention": [
"warn",
{ "selector": "default", "format": ["camelCase"] },
// variableLike
{ "selector": "variable", "format": ["camelCase", "UPPER_CASE"] },
{ "selector": "variable", "filter": "^I.+Service$", "format": ["PascalCase"], "prefix": ["I"] },
// memberLike
{ "selector": "memberLike", "modifiers": ["private"], "format": ["camelCase"], "leadingUnderscore": "require" },
{ "selector": "memberLike", "modifiers": ["protected"], "format": ["camelCase"], "leadingUnderscore": "require" },
{ "selector": "enumMember", "format": ["UPPER_CASE"] },
// memberLike - Allow enum-like objects to use UPPER_CASE
{ "selector": "property", "modifiers": ["public"], "format": ["camelCase", "UPPER_CASE"] },
{ "selector": "method", "modifiers": ["public"], "format": ["camelCase", "UPPER_CASE"] },
// typeLike
{ "selector": "typeLike", "format": ["PascalCase"] },
{ "selector": "interface", "format": ["PascalCase"], "prefix": ["I"] }
],
"@typescript-eslint/prefer-namespace-keyword": "warn",
"@typescript-eslint/type-annotation-spacing": "warn",
"@typescript-eslint/quotes": [
"warn",
"single",
{ "allowTemplateLiterals": true }
],
"@typescript-eslint/semi": [
"warn",
"always"
],
"comma-dangle": [
"warn",
{
"objects": "never",
"arrays": "never",
"functions": "never"
}
],
"curly": [
"warn",
"multi-line"
],
"eol-last": "warn",
"eqeqeq": [
"warn",
"always"
],
"keyword-spacing": "warn",
"new-parens": "warn",
"no-duplicate-imports": "warn",
"no-else-return": [
"warn",
{
"allowElseIf": false
}
],
"no-eval": "warn",
"no-irregular-whitespace": "warn",
"no-restricted-imports": [
"warn",
{
"patterns": [
".*\\/out\\/.*"
]
}
],
"no-trailing-spaces": "warn",
"no-unsafe-finally": "warn",
"no-var": "warn",
"one-var": [
"warn",
"never"
],
"prefer-const": "warn",
"spaced-comment": [
"warn",
"always",
{
"markers": ["/"],
"exceptions": ["-"]
}
]
}
}
-5
View File
@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Question
url: https://stackoverflow.com/questions/tagged/xtermjs
about: Please ask and answer questions here.
+8
View File
@@ -0,0 +1,8 @@
---
name: Question
about: The issue tracker is not for questions. Please ask questions on https://stackoverflow.com/questions/tagged/xtermjs
---
🛑 The issue tracker is not for questions 🛑
If you have a question, please ask it on https://stackoverflow.com/questions/tagged/xtermjs.
+5 -7
View File
@@ -2,9 +2,6 @@ node_modules/
*.swp
.lock-wscript
lib/
out/
out-test/
.nyc_output/
Makefile.gyp
*.Makefile
*.target.gyp.mk
@@ -16,12 +13,13 @@ npm-debug.log
.env
build/
.DS_Store
fixtures/typings-test/*.js
package-lock.json
# Directories needed for code coverage
coverage/
.nyc_output/
# Keep bundled code out of Git
dist/
demo/dist/
# dont commit benchmark folders
.benchmark/
timeline/
+1 -8
View File
@@ -6,9 +6,6 @@
!*.js
!*.json
# Whitelist - css/
!css/**/*.css
# Whitelist - dist/
!dist/**/*.js
!dist/**/*.js.map
@@ -36,13 +33,11 @@
!typings/*.d.ts
# Blacklist - (normal behavior) these will override any whitelist
*.d.ts.map
*.test.ts
*.test.d.ts
*.test.js
*.test.js.map
lib/test/
webpack.config.js
docs/
/.idea/
@@ -50,7 +45,5 @@ docs/
bin/
build/
fixtures/
coverage/
demo/
.devcontainer/
out/
addons/
+3 -6
View File
@@ -13,13 +13,10 @@
"runtimeArgs": [
"--colors",
"--recursive",
"${workspaceRoot}/out/**/*.test.js"
"${workspaceRoot}/lib/**/*.test.js"
],
"env": {
"NODE_PATH": "${workspaceRoot}/out"
},
"sourceMaps": true,
"outFiles": [ "${workspaceRoot}/out/**/*.js" ],
"outFiles": [ "${workspaceRoot}/lib/**/*.js" ],
"internalConsoleOptions": "openOnSessionStart"
},
{
@@ -34,7 +31,7 @@
"runtimeArgs": [
"--colors",
"--recursive",
"${workspaceRoot}/out/**/*.api.js"
"${workspaceRoot}/lib/**/*.api.js"
],
"sourceMaps": true,
"outFiles": [ "${workspaceRoot}/lib/**/*.js" ],
+1 -2
View File
@@ -1,4 +1,3 @@
{
"typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.preferences.quoteStyle": "single"
"typescript.preferences.quoteStyle": "single"
}
-4
View File
@@ -52,7 +52,3 @@ By contributing code to xterm.js you:
holder has explicitly granted the right to use it like this,
through a compatible open source license or through a direct
agreement with you.)
### Test coverage
One area that always needs attention is improving out unit test coverage, you can view the code coverage report on [Azure Pipelines](https://dev.azure.com/xtermjs/xterm.js/_build/latest?definitionId=3) by clicking the Code Coverage tab.
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:12
FROM node:8
MAINTAINER Paris Kasidiaris <paris@sourcelair.com>
# Set the working directory
+33 -39
View File
@@ -1,16 +1,17 @@
# [![xterm.js logo](logo-full.png)](https://xtermjs.org)
[![Build Status](https://dev.azure.com/xtermjs/xterm.js/_apis/build/status/xtermjs.xterm.js)](https://dev.azure.com/xtermjs/xterm.js/_build/latest?definitionId=3)
[![Coverage Status](https://coveralls.io/repos/github/xtermjs/xterm.js/badge.svg?branch=master)](https://coveralls.io/github/xtermjs/xterm.js?branch=master)
Xterm.js is a front-end component written in TypeScript that lets applications bring fully-featured terminals to their users in the browser. It's used by popular projects such as VS Code, Hyper and Theia.
## Features
- **Terminal apps just work**: Xterm.js works with most terminal apps such as `bash`, `vim` and `tmux`, this includes support for curses-based apps and mouse event support.
- **Performant**: Xterm.js is *really* fast, it even includes a GPU-accelerated renderer.
- **Perfomant**: Xterm.js is *really* fast, it even includes a GPU-accelerated renderer.
- **Rich unicode support**: Supports CJK, emojis and IMEs.
- **Self-contained**: Requires zero dependencies to work.
- **Accessible**: Screen reader and minimum contrast ratio support can be turned on
- **Accessible**: Screen reader support can be turned on using the `screenReaderMode` option.
- **And much more**: Links, theming, addons, well documented API, etc.
## What xterm.js is not
@@ -32,8 +33,8 @@ To start using xterm.js on your browser, add the `xterm.js` and `xterm.css` to t
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="node_modules/xterm/css/xterm.css" />
<script src="node_modules/xterm/lib/xterm.js"></script>
<link rel="stylesheet" href="node_modules/xterm/dist/xterm.css" />
<script src="node_modules/xterm/dist/xterm.js"></script>
</head>
<body>
<div id="terminal"></div>
@@ -56,38 +57,43 @@ import { Terminal } from 'xterm';
### Addons
⚠️ *This section describes the new addon format introduced in v3.14.0, see [here](https://github.com/xtermjs/xterm.js/blob/3.14.2/README.md#addons) for the instructions on the old format*
Addons are JavaScript modules that extend the `Terminal` prototype with new methods and attributes to provide additional functionality. There are a handful available in the main repository in the `src/addons` directory and you can even write your own by using the [public API](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts).
Addons are separate modules that extend the `Terminal` by building on the [xterm.js API](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts). To use an addon you first need to install it in your project:
To use an addon, just import the JavaScript module and pass it to `Terminal`'s `applyAddon` method:
```bash
npm i -S xterm-addon-web-links
```
Then import the addon, instantiate it and call `Terminal.loadAddon`:
```ts
```javascript
import { Terminal } from 'xterm';
import { WebLinksAddon } from 'xterm-addon-web-links';
import * as fit from 'xterm/lib/addons/fit/fit';
const terminal = new Terminal();
// Load WebLinksAddon on terminal, this is all that's needed to get web links
// working in the terminal.
terminal.loadAddon(new WebLinksAddon());
Terminal.applyAddon(fit);
var xterm = new Terminal(); // Instantiate the terminal
xterm.fit(); // Use the `fit` method, provided by the `fit` addon
```
The xterm.js team maintains the following addons but they can be built by anyone:
You will also need to include the addon's CSS file if it has one in the folder.
- [`xterm-addon-attach`](https://github.com/xtermjs/xterm.js/tree/master/addons/xterm-addon-attach): Attaches to a server running a process via a websocket
- [`xterm-addon-fit`](https://github.com/xtermjs/xterm.js/tree/master/addons/xterm-addon-fit): Fits the terminal to the containing element
- [`xterm-addon-search`](https://github.com/xtermjs/xterm.js/tree/master/addons/xterm-addon-search): Adds search functionality
- [`xterm-addon-web-links`](https://github.com/xtermjs/xterm.js/tree/master/addons/xterm-addon-web-links): Adds web link detection and interaction
#### Importing Addons in TypeScript
There are currently no typings for addons if they are accessed via extending Terminal prototype, so you will need to upcast if using TypeScript, eg. `(xterm as any).fit()`. Alternatively, you can import the addon function and enhance the terminal on demand. This has better typing support and is friendly to treeshaking.
```typescript
import { Terminal } from 'xterm';
import { fit } from 'xterm/lib/addons/fit/fit';
const xterm = new Terminal();
fit(xterm); // Fit the terminal when necessary
```
## Browser Support
Since xterm.js is typically implemented as a developer tool, only modern browsers are supported officially. Specifically the latest versions of *Chrome*, *Edge*, *Firefox* and *Safari*.
Since xterm.js is typically implemented as a developer tool, only modern browsers are supported officially. Here is a list of the versions we aim to support:
We also partially support *Intenet Explorer 11*, meaning xterm.js should work for the most part, but we reserve the right to not provide workarounds specifically for it unless it's absolutely necessary to get the basic input/output flow working.
- Chrome latest
- Edge latest
- Firefox latest
- Safari latest
- IE11
Xterm.js works seamlessly in [Electron](https://electronjs.org/) apps and may even work on earlier versions of the browsers, these are the versions we strive to keep working.
@@ -110,7 +116,7 @@ Xterm.js is used in several world-class applications to provide great terminal e
- [**WebSSH2**](https://github.com/billchurch/WebSSH2): A web based SSH2 client using xterm.js, socket.io, and ssh2.
- [**Spyder Terminal**](https://github.com/spyder-ide/spyder-terminal): A full fledged system terminal embedded on Spyder IDE.
- [**Cloud Commander**](https://cloudcmd.io "Cloud Commander"): Orthodox web file manager with console and editor.
- [**Next Tech**](https://next.tech "Next Tech"): Online platform for interactive coding and web development courses. Live container-backed terminal uses xterm.js.
- [**Codevolve**](https://www.codevolve.com "Codevolve"): Online platform for interactive coding and web development courses. Live container-backed terminal uses xterm.js.
- [**RStudio**](https://www.rstudio.com/products/RStudio "RStudio"): RStudio is an integrated development environment (IDE) for R.
- [**Terminal for Atom**](https://github.com/jsmecham/atom-terminal-tab): A simple terminal for the Atom text editor.
- [**Eclipse Orion**](https://orionhub.org): A modern, open source software development environment that runs in the cloud. Code, deploy and run in the cloud.
@@ -152,19 +158,7 @@ Xterm.js is used in several world-class applications to provide great terminal e
- [**CodeInterview.io**](https://codeinterview.io): A coding interview platform in 25+ languages and many web frameworks. Uses xterm.js to provide shell access.
- [**Bastillion**](https://www.bastillion.io): Bastillion is an open-source web-based SSH console that centrally manages administrative access to systems.
- [**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.
- [**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
- [**Gus**](https://gus.jp): A shared coding pad where you can run Python with xterm.js
- [**Linode**](https://linode.com): Linode uses xterm.js to provide users a web console for their Linode instances.
- [**FluffOS**](https://www.fluffos.info): Active maintained LPMUD driver with websocket support.
- [**x-terminal**](https://atom.io/packages/x-terminal): Atom plugin for providing terminals inside your Atom workspace.
- [**CoCalc**](https://cocalc.com/): Lots of free software pre-installed, to chat, collaborate, develop, program, publish, research, share, teach, in C++, HTML, Julia, Jupyter, LaTeX, Markdown, Python, R, SageMath, Scala, ...
- [**Dank Domain**](https://www.DDgame.us/): Open source multiuser medieval game supporting old & new terminal emulation.
- [**DockerStacks**](https://docker-stacks.com/): Local LAMP/LEMP development studio
- [**Codecademy**](https://codecademy.com/): Uses xterm.js in its courses on Bash.
- [**Laravel Ssh Web Client**](https://github.com/roke22/Laravel-ssh-client): Laravel server inventory with ssh web client to connect at server using xterm.js
[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 in our list. Note: Please add any new contributions to the end of the list only.
-2
View File
@@ -1,2 +0,0 @@
lib
node_modules
-5
View File
@@ -1,5 +0,0 @@
**/*.api.js
**/*.api.ts
tsconfig.json
.yarnrc
webpack.config.js
-19
View File
@@ -1,19 +0,0 @@
Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-22
View File
@@ -1,22 +0,0 @@
## xterm-addon-attach
An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables attaching to a web socket. This addon requires xterm.js v4+.
### Install
```bash
npm install --save xterm-addon-attach
```
### Usage
```ts
import { Terminal } from 'xterm';
import { AttachAddon } from 'xterm-addon-attach';
const terminal = new Terminal();
const attachAddon = new AttachAddon(webSocket);
terminal.loadAddon(attachAddon);
```
See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/xterm-addon-attach/typings/xterm-addon-attach.d.ts) for more advanced usage.
-21
View File
@@ -1,21 +0,0 @@
{
"name": "xterm-addon-attach",
"version": "0.6.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
},
"main": "lib/xterm-addon-attach.js",
"types": "typings/xterm-addon-attach.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"scripts": {
"build": "../../node_modules/.bin/tsc -p src",
"prepackage": "npm run build",
"package": "../../node_modules/.bin/webpack",
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^4.0.0"
}
}
@@ -1,53 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import WebSocket = require('ws');
import { openTerminal, pollFor, getBrowserType } from '../../../out-test/api/TestUtils';
import { Browser, Page } from 'playwright-core';
const APP = 'http://127.0.0.1:3000/test';
let browser: Browser;
let page: Page;
const width = 800;
const height = 600;
describe('AttachAddon', () => {
before(async function(): Promise<any> {
const browserType = getBrowserType();
browser = await browserType.launch({ dumpio: true,
headless: process.argv.indexOf('--headless') !== -1
});
page = await (await browser.newContext()).newPage();
await page.setViewportSize({ width, height });
});
after(async () => {
await browser.close();
});
beforeEach(async () => await page.goto(APP));
it('string', async function(): Promise<any> {
await openTerminal(page, { rendererType: 'dom' });
const port = 8080;
const server = new WebSocket.Server({ port });
server.on('connection', socket => socket.send('foo'));
await page.evaluate(`window.term.loadAddon(new window.AttachAddon(new WebSocket('ws://localhost:${port}')))`);
await pollFor(page, `window.term.buffer.active.getLine(0).translateToString(true)`, 'foo');
server.close();
});
it('utf8', async function(): Promise<any> {
await openTerminal(page, { rendererType: 'dom' });
const port = 8080;
const server = new WebSocket.Server({ port });
const data = new Uint8Array([102, 111, 111]);
server.on('connection', socket => socket.send(data));
await page.evaluate(`window.term.loadAddon(new window.AttachAddon(new WebSocket('ws://localhost:${port}')))`);
await pollFor(page, `window.term.buffer.active.getLine(0).translateToString(true)`, 'foo');
server.close();
});
});
@@ -1,79 +0,0 @@
/**
* Copyright (c) 2014, 2019 The xterm.js authors. All rights reserved.
* @license MIT
*
* Implements the attach method, that attaches the terminal to a WebSocket stream.
*/
import { Terminal, IDisposable, ITerminalAddon } from 'xterm';
interface IAttachOptions {
bidirectional?: boolean;
}
export class AttachAddon implements ITerminalAddon {
private _socket: WebSocket;
private _bidirectional: boolean;
private _disposables: IDisposable[] = [];
constructor(socket: WebSocket, options?: IAttachOptions) {
this._socket = socket;
// always set binary type to arraybuffer, we do not handle blobs
this._socket.binaryType = 'arraybuffer';
this._bidirectional = (options && options.bidirectional === false) ? false : true;
}
public activate(terminal: Terminal): void {
this._disposables.push(
addSocketListener(this._socket, 'message', ev => {
const data: ArrayBuffer | string = ev.data;
terminal.write(typeof data === 'string' ? data : new Uint8Array(data));
})
);
if (this._bidirectional) {
this._disposables.push(terminal.onData(data => this._sendData(data)));
this._disposables.push(terminal.onBinary(data => this._sendBinary(data)));
}
this._disposables.push(addSocketListener(this._socket, 'close', () => this.dispose()));
this._disposables.push(addSocketListener(this._socket, 'error', () => this.dispose()));
}
public dispose(): void {
this._disposables.forEach(d => d.dispose());
}
private _sendData(data: string): void {
// TODO: do something better than just swallowing
// the data if the socket is not in a working condition
if (this._socket.readyState !== 1) {
return;
}
this._socket.send(data);
}
private _sendBinary(data: string): void {
if (this._socket.readyState !== 1) {
return;
}
const buffer = new Uint8Array(data.length);
for (let i = 0; i < data.length; ++i) {
buffer[i] = data.charCodeAt(i) & 255;
}
this._socket.send(buffer);
}
}
function addSocketListener<K extends keyof WebSocketEventMap>(socket: WebSocket, type: K, handler: (this: WebSocket, ev: WebSocketEventMap[K]) => any): IDisposable {
socket.addEventListener(type, handler);
return {
dispose: () => {
if (!handler) {
// Already disposed
return;
}
socket.removeEventListener(type, handler);
}
};
}
@@ -1,21 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal, ILinkMatcherOptions, ITerminalAddon } from 'xterm';
declare module 'xterm-addon-attach' {
export interface IAttachOptions {
/**
* Whether input should be written to the backend. Defaults to `true`.
*/
bidirectional?: boolean;
}
export class AttachAddon implements ITerminalAddon {
constructor(socket: WebSocket, options?: IAttachOptions);
public activate(terminal: Terminal): void;
public dispose(): void;
}
}
@@ -1,31 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
const path = require('path');
const addonName = 'AttachAddon';
const mainFile = 'xterm-addon-attach.js';
module.exports = {
entry: `./out/${addonName}.js`,
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre",
exclude: /node_modules/
}
]
},
output: {
filename: mainFile,
path: path.resolve('./lib'),
library: addonName,
libraryTarget: 'umd'
},
mode: 'production'
};
-2
View File
@@ -1,2 +0,0 @@
lib
node_modules
-5
View File
@@ -1,5 +0,0 @@
**/*.api.js
**/*.api.ts
tsconfig.json
.yarnrc
webpack.config.js
-19
View File
@@ -1,19 +0,0 @@
Copyright (c) 2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-24
View File
@@ -1,24 +0,0 @@
## xterm-addon-fit
An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables fitting the terminal's dimensions to a containing element. This addon requires xterm.js v4+.
### Install
```bash
npm install --save xterm-addon-fit
```
### Usage
```ts
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
const terminal = new Terminal();
const fitAddon = new FitAddon();
terminal.loadAddon(fitAddon);
terminal.open(containerElement);
fitAddon.fit();
```
See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/xterm-addon-fit/typings/xterm-addon-fit.d.ts) for more advanced usage.
-21
View File
@@ -1,21 +0,0 @@
{
"name": "xterm-addon-fit",
"version": "0.4.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
},
"main": "lib/xterm-addon-fit.js",
"types": "typings/xterm-addon-fit.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"scripts": {
"build": "../../node_modules/.bin/tsc -p src",
"prepackage": "npm run build",
"package": "../../node_modules/.bin/webpack",
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^4.0.0"
}
}
-113
View File
@@ -1,113 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { openTerminal, getBrowserType } from '../../../out-test/api/TestUtils';
import { Browser, Page } from 'playwright-core';
const APP = 'http://127.0.0.1:3000/test';
let browser: Browser;
let page: Page;
const width = 1024;
const height = 768;
describe('FitAddon', () => {
before(async function(): Promise<any> {
const browserType = getBrowserType();
browser = await browserType.launch({ dumpio: true,
headless: process.argv.indexOf('--headless') !== -1
});
page = await (await browser.newContext()).newPage();
await page.setViewportSize({ width, height });
await page.goto(APP);
await openTerminal(page);
});
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();
const dimensions: {cols: number, rows: number} = await page.evaluate(`window.fit.proposeDimensions()`);
assert.equal(dimensions.cols, 87);
assert.isAbove(dimensions.rows, 24);
assert.isBelow(dimensions.rows, 29);
});
it('width', async function(): Promise<any> {
await loadFit(1008);
const dimensions: {cols: number, rows: number} = await page.evaluate(`window.fit.proposeDimensions()`);
assert.equal(dimensions.cols, 110);
assert.isAbove(dimensions.rows, 24);
assert.isBelow(dimensions.rows, 29);
});
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()`);
const cols: number = await page.evaluate(`window.term.cols`);
const rows: number = await page.evaluate(`window.term.rows`);
assert.equal(cols, 87);
assert.isAbove(rows, 24);
assert.isBelow(rows, 29);
});
it('width', async function(): Promise<any> {
await loadFit(1008);
await page.evaluate(`window.fit.fit()`);
const cols: number = await page.evaluate(`window.term.cols`);
const rows: number = await page.evaluate(`window.term.rows`);
assert.equal(cols, 110);
assert.isAbove(rows, 24);
assert.isBelow(rows, 29);
});
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();`);
}
-82
View File
@@ -1,82 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal, ITerminalAddon } from 'xterm';
interface ITerminalDimensions {
/**
* The number of rows in the terminal.
*/
rows: number;
/**
* The number of columns in the terminal.
*/
cols: number;
}
const MINIMUM_COLS = 2;
const MINIMUM_ROWS = 1;
export class FitAddon implements ITerminalAddon {
private _terminal: Terminal | undefined;
constructor() {}
public activate(terminal: Terminal): void {
this._terminal = terminal;
}
public dispose(): void {}
public fit(): void {
const dims = this.proposeDimensions();
if (!dims || !this._terminal) {
return;
}
// TODO: Remove reliance on private API
const core = (this._terminal as any)._core;
// Force a full render
if (this._terminal.rows !== dims.rows || this._terminal.cols !== dims.cols) {
core._renderService.clear();
this._terminal.resize(dims.cols, dims.rows);
}
}
public proposeDimensions(): ITerminalDimensions | undefined {
if (!this._terminal) {
return undefined;
}
if (!this._terminal.element || !this._terminal.element.parentElement) {
return undefined;
}
// TODO: Remove reliance on private API
const core = (this._terminal as any)._core;
const parentElementStyle = window.getComputedStyle(this._terminal.element.parentElement);
const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));
const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')));
const elementStyle = window.getComputedStyle(this._terminal.element);
const elementPadding = {
top: parseInt(elementStyle.getPropertyValue('padding-top')),
bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')),
right: parseInt(elementStyle.getPropertyValue('padding-right')),
left: parseInt(elementStyle.getPropertyValue('padding-left'))
};
const elementPaddingVer = elementPadding.top + elementPadding.bottom;
const elementPaddingHor = elementPadding.right + elementPadding.left;
const availableHeight = parentElementHeight - elementPaddingVer;
const availableWidth = parentElementWidth - elementPaddingHor - core.viewport.scrollBarWidth;
const geometry = {
cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth)),
rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight))
};
return geometry;
}
}
-55
View File
@@ -1,55 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal, ITerminalAddon } from 'xterm';
declare module 'xterm-addon-fit' {
/**
* An xterm.js addon that enables resizing the terminal to the dimensions of
* its containing element.
*/
export class FitAddon implements ITerminalAddon {
/**
* Creates a new fit addon.
*/
constructor();
/**
* Activates the addon
* @param terminal The terminal the addon is being loaded in.
*/
public activate(terminal: Terminal): void;
/**
* Disposes the addon.
*/
public dispose(): void;
/**
* Resizes the terminal to the dimensions of its containing element.
*/
public fit(): void;
/**
* Gets the proposed dimensions that will be used for a fit.
*/
public proposeDimensions(): ITerminalDimensions;
}
/**
* Reprepresents the dimensions of a terminal.
*/
export interface ITerminalDimensions {
/**
* The number of rows in the terminal.
*/
rows: number;
/**
* The number of columns in the terminal.
*/
cols: number;
}
}
-31
View File
@@ -1,31 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
const path = require('path');
const addonName = 'FitAddon';
const mainFile = 'xterm-addon-fit.js';
module.exports = {
entry: `./out/${addonName}.js`,
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre",
exclude: /node_modules/
}
]
},
output: {
filename: mainFile,
path: path.resolve('./lib'),
library: addonName,
libraryTarget: 'umd'
},
mode: 'production'
};
-13
View File
@@ -1,13 +0,0 @@
node_modules/
.nyc_output/
coverage/
lib/
fonts/
.env
.vscode/
*.swp
*.tgz
npm-debug.log*
yarn-error.log*
-48
View File
@@ -1,48 +0,0 @@
# Blacklist - exclude everything except npm defaults such as LICENSE, etc
*
!*/
# Whitelist - entries to be included must be negated with "!"
!*.js
!*.json
# Whitelist - lib/
!lib/**/*.d.ts
!lib/**/*.js
!lib/**/*.js.map
# Whitelist - out/
!out/**/*.d.ts
!out/**/*.js
!out/**/*.js.map
# Whitelist - src/
!src/**/*.ts
!src/**/*.d.ts
!src/**/*.js
!src/**/*.js.map
# Whitelist - typings/
!typings/**/*.d.ts
# Blacklist - (normal behavior) these will override any whitelist
*.test.ts
*.test.d.ts
*.test.js
*.test.js.map
docs/
/.idea/
.vscode/
coverage/
.nyc_output/
fonts/
**/*.api.js
**/*.api.ts
tsconfig.json
.yarnrc
webpack.config.js
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2018
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-53
View File
@@ -1,53 +0,0 @@
## xterm-addon-ligatures
Add support for programming ligatures to [xterm.js] when running in environments with access to [Node.js] APIs (such as [Electron]).
### Requirements
* [Node.js] 8.x or higher (present in [Electron] 1.8.3 or higher)
* [xterm.js] 4.0.0 or higher using the default canvas renderer
### Install
```bash
npm install --save xterm-addon-ligatures
```
### Usage
```ts
import { Terminal } from 'xterm';
import { LigaturesAddon } from 'xterm-addon-ligatures';
const terminal = new Terminal();
const ligaturesAddon = new LigaturesAddon();
terminal.open(containerElement);
terminal.loadAddon(ligaturesAddon);
```
### How It Works
In a browser environment, font ligature information is read directly by the web browser and used to render text correctly without any intervention from the developer. As of version 3, xterm.js uses the canvas to render characters individually, resulting in a significant performance boost. However, this means that it can no longer lean on the browser to determine when to draw font ligatures.
This package locates the font file on disk for the font currently in use by the terminal and parses the ligature information out of it (via the [font-ligatures] package). As text is rendered in xterm.js, this package annotates it with the locations of ligatures, allowing xterm.js to render it correctly.
Since this package depends on being able to find and resolve a system font from disk, it has to have system access that isn't available in the web browser. As a result, this package is mainly useful in environments that combine browser and Node.js runtimes (such as [Electron]).
### Fonts
This package makes use of the following fonts for testing:
* [Fira Code][Fira Code] - [Licensed under the OFL][Fira Code License] by Nikita
Prokopov, Mozilla Foundation with reserved names Fira Code, Fira Mono, and
Fira Sans
* [Iosevka] - [Licensed under the OFL][Iosevka License] by Belleve Invis with
reserved name Iosevka
[xterm.js]: https://github.com/xtermjs/xterm.js
[Electron]: https://electronjs.org/
[Node.js]: https://nodejs.org/
[font-ligatures]: https://github.com/princjef/font-ligatures
[Fira Code]: https://github.com/tonsky/FiraCode
[Fira Code License]: https://github.com/tonsky/FiraCode/blob/master/LICENSE
[Iosevka]: https://github.com/be5invis/Iosevka
[Iosevka License]: https://github.com/be5invis/Iosevka/blob/master/LICENSE.md
@@ -1,75 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
const fs = require('fs');
const path = require('path');
const util = require('util');
const axios = require('axios').default;
const mkdirp = require('mkdirp');
const yauzl = require('yauzl');
const urls = {
fira: 'https://github.com/tonsky/FiraCode/raw/d42e7276fa925e5f82748f3ec9ea429736611b48/distr/otf/FiraCode-Regular.otf',
iosevka: 'https://github.com/be5invis/Iosevka/releases/download/v1.14.3/01-iosevka-1.14.3.zip'
};
const writeFile = util.promisify(fs.writeFile);
const fontsFolder = path.join(__dirname, '../fonts');
async function download() {
await mkdirp(fontsFolder);
await downloadFiraCode();
await downloadIosevka();
console.log('Loaded all fonts for testing')
}
async function downloadFiraCode() {
const file = path.join(fontsFolder, 'firaCode.otf');
if (await util.promisify(fs.exists)(file)) {
console.log('Fira Code already loaded');
} else {
console.log('Downloading Fira Code...');
await writeFile(
file,
(await axios.get(urls.fira, { responseType: 'arraybuffer' })).data
);
}
}
async function downloadIosevka() {
const file = path.join(fontsFolder, 'iosevka.ttf');
if (await util.promisify(fs.exists)(file)) {
console.log('Iosevka already loaded');
} else {
console.log('Downloading Iosevka...');
const iosevkaContents = (await axios.get(urls.iosevka, { responseType: 'arraybuffer' })).data;
const iosevkaZipfile = await util.promisify(yauzl.fromBuffer)(iosevkaContents);
await new Promise((resolve, reject) => {
iosevkaZipfile.on('entry', entry => {
if (entry.fileName === 'ttf/iosevka-regular.ttf') {
iosevkaZipfile.openReadStream(entry, (err, stream) => {
if (err) {
return reject(err);
}
const writeStream = fs.createWriteStream(file);
stream.pipe(writeStream);
writeStream.on('close', () => resolve());
});
}
});
});
}
}
download();
process.on('unhandledRejection', e => {
console.error(e);
process.exit(1);
});
-47
View File
@@ -1,47 +0,0 @@
{
"name": "xterm-addon-ligatures",
"version": "0.3.0",
"description": "Add support for programming ligatures to xterm.js",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
},
"main": "lib/xterm-addon-ligatures.js",
"types": "typings/xterm-addon-ligatures.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"engines": {
"node": ">8.0.0"
},
"scripts": {
"prepare": "node bin/download-fonts.js",
"build": "tsc -p src",
"watch": "tsc -w -p src",
"prepackage": "npm run build",
"package": "webpack",
"pretest": "npm run build",
"test": "nyc mocha out/**/*.test.js",
"prepublish": "npm run package"
},
"keywords": [
"font",
"ligature",
"xterm",
"xterm.js",
"terminal"
],
"license": "MIT",
"dependencies": {
"font-finder": "^1.0.4",
"font-ligatures": "^1.3.3"
},
"devDependencies": {
"@types/sinon": "^5.0.1",
"axios": "^0.18.0",
"mkdirp": "0.5.5",
"sinon": "6.3.5",
"yauzl": "^2.10.0"
},
"peerDependencies": {
"xterm": "^4.0.0"
}
}
@@ -1,23 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal } from 'xterm';
import { enableLigatures } from '.';
export interface ITerminalAddon {
activate(terminal: Terminal): void;
dispose(): void;
}
export class LigaturesAddon implements ITerminalAddon {
constructor() {}
public activate(terminal: Terminal): void {
enableLigatures(terminal);
}
public dispose(): void {}
}
-53
View File
@@ -1,53 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
import * as fontFinder from 'font-finder';
import * as fontLigatures from 'font-ligatures';
import parse from './parse';
let fontsPromise: Promise<fontFinder.FontList> | undefined = undefined;
/**
* Loads the font ligature wrapper for the specified font family if it could be
* resolved, throwing if it is unable to find a suitable match.
* @param fontFamily The CSS font family definition to resolve
* @param cacheSize The size of the ligature cache to maintain if the font is resolved
*/
export default async function load(fontFamily: string, cacheSize: number): Promise<fontLigatures.Font | undefined> {
if (!fontsPromise) {
fontsPromise = fontFinder.list();
}
const fonts = await fontsPromise;
for (const family of parse(fontFamily)) {
// If we reach one of the generic font families, the font resolution
// will end for the browser and we can't determine the specific font
// used. Throw.
if (genericFontFamilies.includes(family)) {
return undefined;
}
if (fonts.hasOwnProperty(family) && fonts[family].length > 0) {
return await fontLigatures.loadFile(fonts[family][0].path, { cacheSize });
}
}
// If none of the fonts could resolve, throw an error
return undefined;
}
// https://drafts.csswg.org/css-fonts-4/#generic-font-families
const genericFontFamilies = [
'serif',
'sans-serif',
'cursive',
'fantasy',
'monospace',
'system-ui',
'emoji',
'math',
'fangsong'
];
@@ -1,222 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
import * as path from 'path';
import * as sinon from 'sinon';
import { assert } from 'chai';
import * as fontFinder from 'font-finder';
import * as fontLigatures from 'font-ligatures';
import * as ligatureSupport from '.';
describe('xterm-addon-ligatures', () => {
let onRefresh: sinon.SinonStub;
let term: MockTerminal;
// -> forms a ligature in Fira Code and Iosevka, but www only forms a ligature
// in Fira Code
const input = 'a -> b www c';
before(() => {
sinon.stub(fontFinder, 'list').returns(Promise.resolve({
// eslint-disable-next-line @typescript-eslint/naming-convention
'Fira Code': [{
path: path.join(__dirname, '../fonts/firaCode.otf'),
style: fontFinder.Style.Regular,
type: fontFinder.Type.Monospace,
weight: 400
}],
// eslint-disable-next-line @typescript-eslint/naming-convention
'Iosevka': [{
path: path.join(__dirname, '../fonts/iosevka.ttf'),
style: fontFinder.Style.Regular,
type: fontFinder.Type.Monospace,
weight: 400
}],
// eslint-disable-next-line @typescript-eslint/naming-convention
'Nonexistant Font': [{
path: path.join(__dirname, '../fonts/nonexistant.ttf'),
style: fontFinder.Style.Regular,
type: fontFinder.Type.Monospace,
weight: 400
}]
} as fontFinder.FontList));
});
beforeEach(() => {
onRefresh = sinon.stub();
term = new MockTerminal(onRefresh);
ligatureSupport.enableLigatures(term as any);
});
it('registers itself correctly', () => {
const term = new MockTerminal(sinon.spy());
assert.isUndefined(term.joiner);
ligatureSupport.enableLigatures(term as any);
assert.isFunction(term.joiner);
});
it('registers itself correctly when called directly', () => {
const term = new MockTerminal(sinon.spy());
assert.isUndefined(term.joiner);
ligatureSupport.enableLigatures(term as any);
assert.isFunction(term.joiner);
});
it('returns an empty set of ranges on the first call while the font is loading', () => {
assert.deepEqual(term.joiner!(input), []);
});
it('returns the correct set of ranges once the font has loaded', done => {
assert.deepEqual(term.joiner!(input), []);
onRefresh.callsFake(() => {
assert.deepEqual(term.joiner!(input), [[2, 4], [7, 10]]);
done();
});
});
it('handles quoted font names', done => {
term.setOption('fontFamily', '"Fira Code", monospace');
assert.deepEqual(term.joiner!(input), []);
onRefresh.callsFake(() => {
assert.deepEqual(term.joiner!(input), [[2, 4], [7, 10]]);
done();
});
});
it('falls back to later fonts if earlier ones are not present', done => {
term.setOption('fontFamily', 'notinstalled, Fira Code, monospace');
assert.deepEqual(term.joiner!(input), []);
onRefresh.callsFake(() => {
assert.deepEqual(term.joiner!(input), [[2, 4], [7, 10]]);
done();
});
});
it('uses the current font value', done => {
// The first three calls are all synchronous so that we don't allow time for
// any fonts to load while we're switching things around
term.setOption('fontFamily', 'Fira Code');
assert.deepEqual(term.joiner!(input), []);
term.setOption('fontFamily', 'notinstalled');
assert.deepEqual(term.joiner!(input), []);
term.setOption('fontFamily', 'Iosevka');
assert.deepEqual(term.joiner!(input), []);
onRefresh.callsFake(() => {
assert.deepEqual(term.joiner!(input), [[2, 4]]);
// And switch it back to Fira Code for good measure
term.setOption('fontFamily', 'Fira Code');
// At this point, we haven't loaded the new font, so the result reverts
// back to empty until that happens
assert.deepEqual(term.joiner!(input), []);
onRefresh.callsFake(() => {
assert.deepEqual(term.joiner!(input), [[2, 4], [7, 10]]);
done();
});
});
});
it('allows multiple terminal instances that use different fonts', done => {
const onRefresh2 = sinon.stub();
const term2 = new MockTerminal(onRefresh2);
term2.setOption('fontFamily', 'Iosevka');
ligatureSupport.enableLigatures(term2 as any);
assert.deepEqual(term.joiner!(input), []);
onRefresh.callsFake(() => {
assert.deepEqual(term.joiner!(input), [[2, 4], [7, 10]]);
assert.deepEqual(term2.joiner!(input), []);
onRefresh2.callsFake(() => {
assert.deepEqual(term2.joiner!(input), [[2, 4]]);
assert.deepEqual(term.joiner!(input), [[2, 4], [7, 10]]);
done();
});
});
});
it('fails if it finds but cannot load the font', async () => {
term.setOption('fontFamily', 'Nonexistant Font, monospace');
assert.deepEqual(term.joiner!(input), []);
await delay(500);
assert.isTrue(onRefresh.notCalled);
assert.throws(() => term.joiner!(input));
});
it('returns nothing if the font is not present on the system', async () => {
term.setOption('fontFamily', 'notinstalled');
assert.deepEqual(term.joiner!(input), []);
await delay(500);
assert.isTrue(onRefresh.notCalled);
assert.deepEqual(term.joiner!(input), []);
});
it('returns nothing if no specific font is specified', async () => {
term.setOption('fontFamily', 'monospace');
assert.deepEqual(term.joiner!(input), []);
await delay(500);
assert.isTrue(onRefresh.notCalled);
assert.deepEqual(term.joiner!(input), []);
});
it('returns nothing if no fonts are provided', async () => {
term.setOption('fontFamily', '');
assert.deepEqual(term.joiner!(input), []);
await delay(500);
assert.isTrue(onRefresh.notCalled);
assert.deepEqual(term.joiner!(input), []);
});
it('fails when given malformed inputs', async () => {
term.setOption('fontFamily', {} as any);
assert.deepEqual(term.joiner!(input), []);
await delay(500);
assert.isTrue(onRefresh.notCalled);
assert.throws(() => term.joiner!(input));
});
it('ensures no empty errors are thrown', async () => {
sinon.stub(fontLigatures, 'loadFile').callsFake(async () => { throw undefined; });
term.setOption('fontFamily', 'Iosevka');
assert.deepEqual(term.joiner!(input), []);
await delay(500);
assert.isTrue(onRefresh.notCalled);
assert.throws(() => term.joiner!(input), 'Failure while loading font');
(fontLigatures.loadFile as sinon.SinonStub).restore();
});
});
class MockTerminal {
private _options: { [name: string]: string | number } = {
fontFamily: 'Fira Code, monospace',
rows: 50
};
public joiner?: (text: string) => [number, number][];
public refresh: (start: number, end: number) => void;
constructor(onRefresh: (start: number, end: number) => void) {
this.refresh = onRefresh;
}
public registerCharacterJoiner(handler: (text: string) => [number, number][]): number {
this.joiner = handler;
return 1;
}
public deregisterCharacterJoiner(id: number): void {
this.joiner = undefined;
}
public setOption(name: string, value: string | number): void {
this._options[name] = value;
}
public getOption(name: string): string | number {
return this._options[name];
}
}
function delay(delayMs: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, delayMs));
}
-85
View File
@@ -1,85 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal } from 'xterm';
import { Font } from 'font-ligatures';
import load from './font';
const enum LoadingState {
UNLOADED,
LOADING,
LOADED,
FAILED
}
// Caches 100K characters worth of ligatures. In practice this works out to
// about 650 KB worth of cache, when a moderate number of ligatures are present.
const CACHE_SIZE = 100000;
/**
* Enable ligature support for the provided Terminal instance. To function
* properly, this must be called after `open()` is called on the therminal. If
* the font currently in use supports ligatures, the terminal will automatically
* start to render them.
* @param term Terminal instance from xterm.js
*/
export function enableLigatures(term: Terminal): void {
let currentFontName: string | undefined = undefined;
let font: Font | undefined = undefined;
let loadingState: LoadingState = LoadingState.UNLOADED;
let loadError: any | undefined = undefined;
term.registerCharacterJoiner((text: string): [number, number][] => {
// If the font hasn't been loaded yet, load it and return an empty result
const termFont = term.getOption('fontFamily');
if (
termFont &&
(loadingState === LoadingState.UNLOADED || currentFontName !== termFont)
) {
font = undefined;
loadingState = LoadingState.LOADING;
currentFontName = termFont;
const currentCallFontName = currentFontName;
load(currentCallFontName, CACHE_SIZE)
.then(f => {
// Another request may have come in while we were waiting, so make
// sure our font is still vaild.
if (currentCallFontName === term.getOption('fontFamily')) {
loadingState = LoadingState.LOADED;
font = f;
// Only refresh things if we actually found a font
if (f) {
term.refresh(0, term.getOption('rows') - 1);
}
}
})
.catch(e => {
// Another request may have come in while we were waiting, so make
// sure our font is still vaild.
if (currentCallFontName === term.getOption('fontFamily')) {
loadingState = LoadingState.FAILED;
font = undefined;
loadError = e;
}
});
}
if (font && loadingState === LoadingState.LOADED) {
// We clone the entries to avoid the internal cache of the ligature finder
// getting messed up.
return font.findLigatureRanges(text).map<[number, number]>(
range => [range[0], range[1]]
);
}
if (loadingState === LoadingState.FAILED) {
throw loadError || new Error('Failure while loading font');
}
return [];
});
}
@@ -1,75 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import parse from './parse';
// TODO: integrate tests from http://test.csswg.org/suites/css-fonts-4_dev/nightly-unstable/
describe('parse', () => {
it('parses individual families', () => {
assert.deepEqual(parse('monospace'), ['monospace']);
});
it('parses multiple families', () => {
assert.deepEqual(parse('Arial, Verdana, serif'), ['Arial', 'Verdana', 'serif']);
});
it('parses quoted families', () => {
assert.deepEqual(parse('"Times New Roman", serif'), ['Times New Roman', 'serif']);
});
it('parses single quoted families', () => {
assert.deepEqual(parse('\'Times New Roman\', serif'), ['Times New Roman', 'serif']);
});
it('parses families with spaces in their names', () => {
assert.deepEqual(parse('Times New Roman, serif'), ['Times New Roman', 'serif']);
});
it('collapses multiple spaces together in identifiers', () => {
assert.deepEqual(parse('Times New Roman, serif'), ['Times New Roman', 'serif']);
});
it('does not collapse multiple spaces together in quoted strings', () => {
assert.deepEqual(parse('"Times New Roman", serif'), ['Times New Roman', 'serif']);
});
it('handles escaped characters in strings', () => {
assert.deepEqual(parse('"quote \\" slash \\\\ slashquote \\\\\\"", serif'), ['quote " slash \\ slashquote \\"', 'serif']);
});
it('fails if a family has an unterminated string', () => {
assert.throws(() => parse('"Unterminated, serif'));
});
it('handles unicode escape sequences', () => {
assert.deepEqual(parse('"space\\20 between", serif'), ['space between', 'serif']);
});
it('swallows only the first space after a unicode escape', () => {
assert.deepEqual(parse('"two-space\\20 between", serif'), ['two-space between', 'serif']);
});
it('automatically ends the unicode escape after six digits', () => {
assert.deepEqual(parse('space\\000020between, serif'), ['space between', 'serif']);
});
it('handles unicode escapes at the end of the family', () => {
assert.deepEqual(parse('endswithbrace \\7b, serif'), ['endswithbrace {', 'serif']);
});
it('handles unicode escapes at the end of the input', () => {
assert.deepEqual(parse('endswithbrace \\7b'), ['endswithbrace {']);
});
it('handles other escaped characters in identifiers', () => {
assert.deepEqual(parse('has\\,comma'), ['has,comma']);
});
it('swallows escaped newlines in strings', () => {
assert.deepEqual(parse('"multi \\\nline", serif'), ['multi line', 'serif']);
});
});
-180
View File
@@ -1,180 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
interface IParseContext {
input: string;
offset: number;
}
/**
* Parses a CSS font family value, returning the component font families
* contained within.
*
* @param family The CSS font family input string to parse
*/
export default function parse(family: string): string[] {
if (typeof family !== 'string') {
throw new Error('Font family must be a string');
}
const context: IParseContext = {
input: family,
offset: 0
};
const families = [];
let currentFamily = '';
// Work through the input character by character until there are none left.
// This lexing and parsing in one pass.
while (context.offset < context.input.length) {
const char = context.input[context.offset++];
switch (char) {
// String
case '\'':
case '"':
currentFamily += parseString(context, char);
break;
// End of family
case ',':
families.push(currentFamily);
currentFamily = '';
break;
default:
// Identifiers (whitespace between families is swallowed)
if (!/\s/.test(char)) {
context.offset--;
currentFamily += parseIdentifier(context);
families.push(currentFamily);
currentFamily = '';
}
}
}
return families;
}
/**
* Parse a CSS string.
*
* @param context Parsing input and offset
* @param quoteChar The quote character for the string (' or ")
*/
function parseString(context: IParseContext, quoteChar: '\'' | '"'): string {
let str = '';
let escaped = false;
while (context.offset < context.input.length) {
const char = context.input[context.offset++];
if (escaped) {
if (/[0-9a-fA-F]/.test(char)) {
// Unicode escape
context.offset--;
str += parseUnicode(context);
} else if (char !== '\n') {
// Newlines are ignored if escaped. Other characters are used as is.
str += char;
}
escaped = false;
} else {
switch (char) {
// Terminated quote
case quoteChar:
return str;
// Begin escape
case '\\':
escaped = true;
break;
// Add character to string
default:
str += char;
}
}
}
throw new Error('Unterminated string');
}
/**
* Parse a CSS custom identifier.
*
* @param context Parsing input and offset
*/
function parseIdentifier(context: IParseContext): string {
let str = '';
let escaped = false;
while (context.offset < context.input.length) {
const char = context.input[context.offset++];
if (escaped) {
if (/[0-9a-fA-F]/.test(char)) {
// Unicode escape
context.offset--;
str += parseUnicode(context);
} else {
// Everything else is used as is
str += char;
}
escaped = false;
} else {
switch (char) {
// Begin escape
case '\\':
escaped = true;
break;
// Terminate identifier
case ',':
return str;
default:
if (/\s/.test(char)) {
// Whitespace is collapsed into a single space within an identifier
if (!str.endsWith(' ')) {
str += ' ';
}
} else {
// Add other characters directly
str += char;
}
}
}
}
return str;
}
/**
* Parse a CSS unicode escape.
*
* @param context Parsing input and offset
*/
function parseUnicode(context: IParseContext): string {
let str = '';
while (context.offset < context.input.length) {
const char = context.input[context.offset++];
if (/\s/.test(char)) {
// The first whitespace character after a unicode escape indicates the end
// of the escape and is swallowed.
return unicodeToString(str);
}
if (str.length >= 6 || !/[0-9a-fA-F]/.test(char)) {
// If the next character is not a valid hex digit or we have reached the
// maximum of 6 digits in the escape, terminate the escape.
context.offset--;
return unicodeToString(str);
}
// Otherwise, just add it to the escape
str += char;
}
return unicodeToString(str);
}
/**
* Convert a unicode code point from a hex string to a utf8 string.
*
* @param codePoint Unicode code point represented as a hex string
*/
function unicodeToString(codePoint: string): string {
return String.fromCodePoint(parseInt(codePoint, 16));
}
@@ -1,16 +0,0 @@
{
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"sourceMap": true,
"outDir": "../out",
"rootDir": ".",
"strict": true,
"noUnusedLocals": true,
"preserveWatchOutput": true
},
"include": [
"./**/*",
"../../../typings/xterm.d.ts"
]
}
@@ -1,34 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*
* This contains the type declarations for the xterm-addon-ligatures library.
* Note that some interfaces may differ between this file and the actual
* implementation in src/, that's because this file declares the *public* API
* which is intended to be stable and consumed by external programs.
*/
import { Terminal, ITerminalAddon } from 'xterm';
declare module 'xterm-addon-ligatures' {
/**
* An xterm.js addon that enables web links.
*/
export class LigaturesAddon implements ITerminalAddon {
/**
* Creates a new ligatures addon.
*/
constructor();
/**
* Activates the addon
* @param terminal The terminal the addon is being loaded in.
*/
public activate(terminal: Terminal): void;
/**
* Disposes the addon.
*/
public dispose(): void;
}
}
@@ -1,36 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
const path = require('path');
const addonName = 'LigaturesAddon';
const mainFile = 'xterm-addon-ligatures.js';
module.exports = {
entry: `./out/${addonName}.js`,
devtool: 'source-map',
target: 'electron-renderer',
module: {
rules: [
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre",
exclude: /node_modules/
}
]
},
output: {
filename: mainFile,
path: path.resolve('./lib'),
library: addonName,
libraryTarget: 'umd'
},
mode: 'production',
externals: {
'font-finder':'font-finder',
'font-ligatures':'font-ligatures'
}
};
-236
View File
@@ -1,236 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@sinonjs/commons@^1", "@sinonjs/commons@^1.0.2", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.7.0":
version "1.8.0"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.0.tgz#c8d68821a854c555bba172f3b06959a0039b236d"
integrity sha512-wEj54PfsZ5jGSwMX68G8ZXFawcSglQSXqCftWX3ec8MDUzQdHgcKvw97awHbY0efQEL5iKUOAmmVtoYgmrSG4Q==
dependencies:
type-detect "4.0.8"
"@sinonjs/formatio@^3.0.0", "@sinonjs/formatio@^3.2.1":
version "3.2.2"
resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.2.tgz#771c60dfa75ea7f2d68e3b94c7e888a78781372c"
integrity sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==
dependencies:
"@sinonjs/commons" "^1"
"@sinonjs/samsam" "^3.1.0"
"@sinonjs/samsam@^2.1.2":
version "2.1.3"
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-2.1.3.tgz#62cf2a9b624edc795134135fe37fc2ae8ea36be3"
integrity sha512-8zNeBkSKhU9a5cRNbpCKau2WWPfan+Q2zDlcXvXyhn9EsMqgYs4qzo0XHNVlXC6ABQL8fT6nV+zzo5RTHJzyXw==
"@sinonjs/samsam@^3.1.0":
version "3.3.3"
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.3.tgz#46682efd9967b259b81136b9f120fd54585feb4a"
integrity sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==
dependencies:
"@sinonjs/commons" "^1.3.0"
array-from "^2.1.1"
lodash "^4.17.15"
"@sinonjs/text-encoding@^0.7.1":
version "0.7.1"
resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5"
integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==
"@types/sinon@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-5.0.1.tgz#a15b36ec42f1f53166617491feabd1734cb03e21"
array-from@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195"
integrity sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=
axios@^0.18.0:
version "0.18.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3"
dependencies:
follow-redirects "1.5.10"
is-buffer "^2.0.2"
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
debug@=3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
ms "2.0.0"
diff@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
fd-slicer@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
dependencies:
pend "~1.2.0"
follow-redirects@1.5.10:
version "1.5.10"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
dependencies:
debug "=3.1.0"
font-finder@^1.0.3, font-finder@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/font-finder/-/font-finder-1.0.4.tgz#2ca944954dd8d0e1b5bdc4c596cc08607761d89b"
dependencies:
get-system-fonts "^2.0.0"
promise-stream-reader "^1.0.1"
font-ligatures@^1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/font-ligatures/-/font-ligatures-1.3.3.tgz#63fff18dc8adb3a11fe5eec1f4e8d7edfa8075b9"
integrity sha512-NSGpHgVNX81M7AWS1XylK1UZbN3QllfUIDAAuPv6TUcl5O2b781JcKS5L2RopAU0AqlTyX3ZuX/04eaMpbVrHA==
dependencies:
font-finder "^1.0.3"
lru-cache "^4.1.3"
opentype.js "^0.8.0"
get-system-fonts@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/get-system-fonts/-/get-system-fonts-2.0.0.tgz#a43b9a33f05c0715a60176d2aad5ce6e98f0a3c6"
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
is-buffer@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725"
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
just-extend@^4.0.2:
version "4.1.0"
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.0.tgz#7278a4027d889601640ee0ce0e5a00b992467da4"
integrity sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
lodash@^4.17.15:
version "4.17.19"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
lolex@^2.7.5:
version "2.7.5"
resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.7.5.tgz#113001d56bfc7e02d56e36291cc5c413d1aa0733"
integrity sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==
lolex@^5.0.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367"
integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==
dependencies:
"@sinonjs/commons" "^1.7.0"
lru-cache@^4.1.3:
version "4.1.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c"
dependencies:
pseudomap "^1.0.2"
yallist "^2.1.2"
minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
mkdirp@0.5.5:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
minimist "^1.2.5"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
nise@^1.4.5:
version "1.5.3"
resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.3.tgz#9d2cfe37d44f57317766c6e9408a359c5d3ac1f7"
integrity sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==
dependencies:
"@sinonjs/formatio" "^3.2.1"
"@sinonjs/text-encoding" "^0.7.1"
just-extend "^4.0.2"
lolex "^5.0.1"
path-to-regexp "^1.7.0"
opentype.js@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/opentype.js/-/opentype.js-0.8.0.tgz#acabcfa1642fbe894a3e4d759e43ba694e02bd35"
dependencies:
tiny-inflate "^1.0.2"
path-to-regexp@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
dependencies:
isarray "0.0.1"
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
promise-stream-reader@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/promise-stream-reader/-/promise-stream-reader-1.0.1.tgz#4e793a79c9d49a73ccd947c6da9c127f12923649"
pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
sinon@6.3.5:
version "6.3.5"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-6.3.5.tgz#0f6d6a5b4ebaad1f6e8e019395542d1d02c144a0"
integrity sha512-xgoZ2gKjyVRcF08RrIQc+srnSyY1JDJtxu3Nsz07j1ffjgXoY6uPLf/qja6nDBZgzYYEovVkFryw2+KiZz11xQ==
dependencies:
"@sinonjs/commons" "^1.0.2"
"@sinonjs/formatio" "^3.0.0"
"@sinonjs/samsam" "^2.1.2"
diff "^3.5.0"
lodash.get "^4.4.2"
lolex "^2.7.5"
nise "^1.4.5"
supports-color "^5.5.0"
type-detect "^4.0.8"
supports-color@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
tiny-inflate@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.2.tgz#93d9decffc8805bd57eae4310f0b745e9b6fb3a7"
type-detect@4.0.8, type-detect@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
yallist@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
yauzl@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"
-2
View File
@@ -1,2 +0,0 @@
lib
node_modules
-5
View File
@@ -1,5 +0,0 @@
**/*.api.js
**/*.api.ts
tsconfig.json
.yarnrc
webpack.config.js
-19
View File
@@ -1,19 +0,0 @@
Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-23
View File
@@ -1,23 +0,0 @@
## xterm-addon-search
An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables searching the buffer. This addon requires xterm.js v4+.
### Install
```bash
npm install --save xterm-addon-search
```
### Usage
```ts
import { Terminal } from 'xterm';
import { SearchAddon } from 'xterm-addon-search';
const terminal = new Terminal();
const searchAddon = new SearchAddon();
terminal.loadAddon(searchAddon);
searchAddon.findNext('foo');
```
See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/xterm-addon-search/typings/xterm-addon-search.d.ts) for more advanced usage.
@@ -1,70 +0,0 @@
/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles
make[3]: Entering directory `/home/yan/ssd/searchtrunk/tmp/release'
make[3]: Nothing to be done for `libsrc/wrap_curl/CMakeFiles/wrap_curl.dir/build'.
make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release'
[ 0%] /usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles
[ 0%] [ 0%] /usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles
Built target config_utils
[ 0%] make[3]: Entering directory `/home/yan/ssd/searchtrunk/tmp/release'
/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles
Built target omega
make[3]: Entering directory `/home/yan/ssd/searchtrunk/tmp/release'
cd /home/yan/ssd/searchtrunk/tmp/release && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/yan/ssd/searchtrunk /home/yan/ssd/searchtrunk/projects/protogen/cpp /home/yan/ssd/searchtrunk/tmp/release /home/yan/ssd/searchtrunk/tmp/release/projects/protogen/cpp /home/yan/ssd/searchtrunk/tmp/release/projects/protogen/cpp/CMakeFiles/protogen.dir/DependInfo.cmake --color=
/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles
Built target morpheus
make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release'
make -f libsrc/base/CMakeFiles/base.dir/build.make libsrc/base/CMakeFiles/base.dir/build
make[3]: Entering directory `/home/yan/ssd/searchtrunk/tmp/release'
make[3]: Nothing to be done for `libsrc/coroutine/CMakeFiles/coroutine.dir/build'.
Built target wrap_curl
make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release'
/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles
/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles
[ 0%] [ 0%] [ 0%] [ 0%] Building CXX object libsrc/image_crop/CMakeFiles/image_crop.dir/src/crop_context.cpp.o
cd /home/yan/ssd/searchtrunk/tmp/release/libsrc/image_crop && /opt/rh/devtoolset-7/root/usr/bin/c++ -DGOGO_USE_LIBEVENT2=1 -DGOGO_USE_OPENSSL_SNI=1 -DHAVE_BOOST -DHAVE_GLIBC -DHAVE_MALLOC_TRIM -DHAVE_PREAD -DHAVE_READAHEAD -D_FILE_OFFSET_BITS=64 -D_STAT_LEMM -pipe -Wall -Wextra -Werror=multichar -Wno-deprecated -Wno-unused-parameter -pthread -fPIC -Woverloaded-virtual -Wnon-virtual-dtor -Werror -std=gnu++11 -Wno-unknown-pragmas -DBOOST_FILESYSTEM_VERSION=3 -DBOOST_NO_CXX11_HDR_CODECVT -DMAGICKCORE_QUANTUM_DEPTH=8 -DMAGICKCORE_HDRI_ENABLE=0 -Wno-unused-local-typedefs -D_GLIBCXX_USE_CXX11_ABI=0 -std=gnu++14 -DCXXHASH128_EXIST -static-libstdc++ -Wno-implicit-fallthrough -Wno-stringop-overflow -O3 -DNDEBUG -DBOOST_UBLAS_NDEBUG -g0 -march=core2 -mssse3 -msse4.1 -I/home/yan/ssd/searchtrunk/tmp/release/__inc -I/home/yan/ssd/searchtrunk/libsrc/image_crop/src -I/home/yan/ssd/searchtrunk/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/boost_1_69_0/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/icu-49.1.2/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/protobuf-3.4.0/include -I/home/yan/ssd/searchtrunk/tmp/release -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/opencv-3.0.0m2/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/dlib-19.7p1/include -Wno-array-bounds -o CMakeFiles/image_crop.dir/src/crop_context.cpp.o -c /home/yan/ssd/searchtrunk/libsrc/image_crop/src/crop_context.cpp
Building CXX object libsrc/image_crop/CMakeFiles/image_crop.dir/src/detector.cpp.o
Building CXX object libsrc/image_crop/CMakeFiles/image_crop.dir/src/image_crop.cpp.o
cd /home/yan/ssd/searchtrunk/tmp/release/libsrc/image_crop && /opt/rh/devtoolset-7/root/usr/bin/c++ -DGOGO_USE_LIBEVENT2=1 -DGOGO_USE_OPENSSL_SNI=1 -DHAVE_BOOST -DHAVE_GLIBC -DHAVE_MALLOC_TRIM -DHAVE_PREAD -DHAVE_READAHEAD -D_FILE_OFFSET_BITS=64 -D_STAT_LEMM -pipe -Wall -Wextra -Werror=multichar -Wno-deprecated -Wno-unused-parameter -pthread -fPIC -Woverloaded-virtual -Wnon-virtual-dtor -Werror -std=gnu++11 -Wno-unknown-pragmas -DBOOST_FILESYSTEM_VERSION=3 -DBOOST_NO_CXX11_HDR_CODECVT -DMAGICKCORE_QUANTUM_DEPTH=8 -DMAGICKCORE_HDRI_ENABLE=0 -Wno-unused-local-typedefs -D_GLIBCXX_USE_CXX11_ABI=0 -std=gnu++14 -DCXXHASH128_EXIST -static-libstdc++ -Wno-implicit-fallthrough -Wno-stringop-overflow -O3 -DNDEBUG -DBOOST_UBLAS_NDEBUG -g0 -march=core2 -mssse3 -msse4.1 -I/home/yan/ssd/searchtrunk/tmp/release/__inc -I/home/yan/ssd/searchtrunk/libsrc/image_crop/src -I/home/yan/ssd/searchtrunk/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/boost_1_69_0/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/icu-49.1.2/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/protobuf-3.4.0/include -I/home/yan/ssd/searchtrunk/tmp/release -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/opencv-3.0.0m2/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/dlib-19.7p1/include -Wno-array-bounds -o CMakeFiles/image_crop.dir/src/detector.cpp.o -c /home/yan/ssd/searchtrunk/libsrc/image_crop/src/detector.cpp
Built target coroutine
cd /home/yan/ssd/searchtrunk/tmp/release/libsrc/image_crop && /opt/rh/devtoolset-7/root/usr/bin/c++ -DGOGO_USE_LIBEVENT2=1 -DGOGO_USE_OPENSSL_SNI=1 -DHAVE_BOOST -DHAVE_GLIBC -DHAVE_MALLOC_TRIM -DHAVE_PREAD -DHAVE_READAHEAD -D_FILE_OFFSET_BITS=64 -D_STAT_LEMM -pipe -Wall -Wextra -Werror=multichar -Wno-deprecated -Wno-unused-parameter -pthread -fPIC -Woverloaded-virtual -Wnon-virtual-dtor -Werror -std=gnu++11 -Wno-unknown-pragmas -DBOOST_FILESYSTEM_VERSION=3 -DBOOST_NO_CXX11_HDR_CODECVT -DMAGICKCORE_QUANTUM_DEPTH=8 -DMAGICKCORE_HDRI_ENABLE=0 -Wno-unused-local-typedefs -D_GLIBCXX_USE_CXX11_ABI=0 -std=gnu++14 -DCXXHASH128_EXIST -static-libstdc++ -Wno-implicit-fallthrough -Wno-stringop-overflow -O3 -DNDEBUG -DBOOST_UBLAS_NDEBUG -g0 -march=core2 -mssse3 -msse4.1 -I/home/yan/ssd/searchtrunk/tmp/release/__inc -I/home/yan/ssd/searchtrunk/libsrc/image_crop/src -I/home/yan/ssd/searchtrunk/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/boost_1_69_0/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/icu-49.1.2/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/protobuf-3.4.0/include -I/home/yan/ssd/searchtrunk/tmp/release -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/opencv-3.0.0m2/include -isystem /home/yan/ssd/searchtrunk/tmp/release/contrib/dlib-19.7p1/include -Wno-array-bounds -o CMakeFiles/image_crop.dir/src/image_crop.cpp.o -c /home/yan/ssd/searchtrunk/libsrc/image_crop/src/image_crop.cpp
make[3]: Entering directory `/home/yan/ssd/searchtrunk/tmp/release'
make[3]: Nothing to be done for `libsrc/base/CMakeFiles/base.dir/build'.
make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release'
/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles
make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release'
make -f projects/protogen/cpp/CMakeFiles/protogen.dir/build.make projects/protogen/cpp/CMakeFiles/protogen.dir/build
[ 0%] Built target base
In file included from /home/yan/ssd/searchtrunk/include/image_crop/detector.hpp:3:0,
from /home/yan/ssd/searchtrunk/libsrc/image_crop/src/detector.cpp:2:
/home/yan/ssd/searchtrunk/include/image_crop/crop_context.hpp:6:10: fatal error: opencv2/dnn.hpp: No such file or directory
#include <opencv2/dnn.hpp>
^~~~~~~~~~~~~~~~~
compilation terminated.
make[3]: *** [libsrc/image_crop/CMakeFiles/image_crop.dir/src/detector.cpp.o] Error 1
make[3]: *** Waiting for unfinished jobs....
In file included from /home/yan/ssd/searchtrunk/include/image_crop/image_crop.hpp:3:0,
from /home/yan/ssd/searchtrunk/libsrc/image_crop/src/image_crop.cpp:1:
/home/yan/ssd/searchtrunk/include/image_crop/crop_context.hpp:6:10: fatal error: opencv2/dnn.hpp: No such file or directory
#include <opencv2/dnn.hpp>
^~~~~~~~~~~~~~~~~
compilation terminated.
make[3]: *** [libsrc/image_crop/CMakeFiles/image_crop.dir/src/image_crop.cpp.o] Error 1
In file included from /home/yan/ssd/searchtrunk/libsrc/image_crop/src/crop_context.cpp:1:0:
/home/yan/ssd/searchtrunk/include/image_crop/crop_context.hpp:6:10: fatal error: opencv2/dnn.hpp: No such file or directory
#include <opencv2/dnn.hpp>
^~~~~~~~~~~~~~~~~
compilation terminated.
make[3]: *** [libsrc/image_crop/CMakeFiles/image_crop.dir/src/crop_context.cpp.o] Error 1
make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release'
make[2]: *** [libsrc/image_crop/CMakeFiles/image_crop.dir/all] Error 2
make[2]: *** Waiting for unfinished jobs....
make[3]: Entering directory `/home/yan/ssd/searchtrunk/tmp/release'
make[3]: Nothing to be done for `projects/protogen/cpp/CMakeFiles/protogen.dir/build'.
make[3]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release'
/usr/bin/cmake -E cmake_progress_report /home/yan/ssd/searchtrunk/tmp/release/CMakeFiles 63 64 65 66
[ 25%] Built target protogen
make[2]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release'
make[1]: *** [all] Error 2
make[1]: Leaving directory `/home/yan/ssd/searchtrunk/tmp/release/projects/projectX'
make: *** [install] Error 2
yan@yPC:~/ssd/searchtrunk/projects/projectX$
-21
View File
@@ -1,21 +0,0 @@
{
"name": "xterm-addon-search",
"version": "0.7.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
},
"main": "lib/xterm-addon-search.js",
"types": "typings/xterm-addon-search.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"scripts": {
"build": "../../node_modules/.bin/tsc -p src",
"prepackage": "npm run build",
"package": "../../node_modules/.bin/webpack",
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^4.0.0"
}
}
@@ -1,197 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { readFile } from 'fs';
import { resolve } from 'path';
import { openTerminal, writeSync, getBrowserType } from '../../../out-test/api/TestUtils';
import { Browser, Page } from 'playwright-core';
const APP = 'http://127.0.0.1:3000/test';
let browser: Browser;
let page: Page;
const width = 800;
const height = 600;
describe('Search Tests', function(): void {
before(async function(): Promise<any> {
const browserType = getBrowserType();
browser = await browserType.launch({ dumpio: true,
headless: process.argv.indexOf('--headless') !== -1
});
page = await (await browser.newContext()).newPage();
await page.setViewportSize({ width, height });
await page.goto(APP);
await openTerminal(page);
await page.evaluate(`window.search = new SearchAddon();`);
await page.evaluate(`window.term.loadAddon(window.search);`);
});
after(() => {
browser.close();
});
beforeEach(async () => {
await page.evaluate(`window.term.reset()`);
});
it('Simple Search', async () => {
await writeSync(page, 'dafhdjfldshafhldsahfkjhldhjkftestlhfdsakjfhdjhlfdsjkafhjdlk');
assert.deepEqual(await page.evaluate(`window.search.findNext('test')`), true);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'test');
});
it('Scrolling Search', async () => {
let dataString = '';
for (let i = 0; i < 100; i++) {
if (i === 52) {
dataString += '$^1_3{}test$#';
}
dataString += makeData(50);
}
await writeSync(page, dataString);
assert.deepEqual(await page.evaluate(`window.search.findNext('$^1_3{}test$#')`), true);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), '$^1_3{}test$#');
});
it('Incremental Find Previous', async () => {
await page.evaluate(`window.term.writeln('package.jsonc\\n')`);
await writeSync(page, 'package.json pack package.lock');
await page.evaluate(`window.search.findPrevious('pack', {incremental: true})`);
let line: string = await page.evaluate(`window.term.buffer.active.getLine(window.term.getSelectionPosition().startRow).translateToString()`);
let selectionPosition: { startColumn: number, startRow: number, endColumn: number, endRow: number } = await page.evaluate(`window.term.getSelectionPosition()`);
// We look further ahead in the line to ensure that pack was selected from package.lock
assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn + 8), 'package.lock');
await page.evaluate(`window.search.findPrevious('package.j', {incremental: true})`);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn + 3), 'package.json');
await page.evaluate(`window.search.findPrevious('package.jsonc', {incremental: true})`);
// We have to reevaluate line because it should have switched starting rows at this point
line = await page.evaluate(`window.term.buffer.active.getLine(window.term.getSelectionPosition().startRow).translateToString()`);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn), 'package.jsonc');
});
it('Incremental Find Next', async () => {
await page.evaluate(`window.term.writeln('package.lock pack package.json package.ups\\n')`);
await writeSync(page, 'package.jsonc');
await page.evaluate(`window.search.findNext('pack', {incremental: true})`);
let line: string = await page.evaluate(`window.term.buffer.active.getLine(window.term.getSelectionPosition().startRow).translateToString()`);
let selectionPosition: { startColumn: number, startRow: number, endColumn: number, endRow: number } = await page.evaluate(`window.term.getSelectionPosition()`);
// We look further ahead in the line to ensure that pack was selected from package.lock
assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn + 8), 'package.lock');
await page.evaluate(`window.search.findNext('package.j', {incremental: true})`);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn + 3), 'package.json');
await page.evaluate(`window.search.findNext('package.jsonc', {incremental: true})`);
// We have to reevaluate line because it should have switched starting rows at this point
line = await page.evaluate(`window.term.buffer.active.getLine(window.term.getSelectionPosition().startRow).translateToString()`);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(line.substring(selectionPosition.startColumn, selectionPosition.endColumn), 'package.jsonc');
});
it('Simple Regex', async () => {
await writeSync(page, 'abc123defABCD');
await page.evaluate(`window.search.findNext('[a-z]+', {regex: true})`);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc');
await page.evaluate(`window.search.findNext('[A-Z]+', {regex: true, caseSensitive: true})`);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'ABCD');
});
it('Search for single result twice should not unselect it', async () => {
await writeSync(page, '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');
});
describe('Regression tests', () => {
describe('#2444 wrapped line content not being found', () => {
let fixture: string;
before(async () => {
const rawFixture = await new Promise<Buffer>(r => readFile(resolve(__dirname, '../fixtures/issue-2444'), (err, data) => r(data)));
fixture = rawFixture.toString()
.replace(/\n/g, '\\n\\r')
.replace(/'/g, '\\\'');
});
it('should find all occurrences using findNext', async () => {
await writeSync(page, fixture);
assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true);
let selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 53, endColumn: 30, endRow: 53 });
assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 76, endColumn: 30, endRow: 76 });
assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 96, endColumn: 30, endRow: 96 });
assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 1, startRow: 114, endColumn: 7, endRow: 114 });
assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 115, endColumn: 17, endRow: 115 });
assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 1, startRow: 126, endColumn: 7, endRow: 126 });
assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 127, endColumn: 17, endRow: 127 });
assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 1, startRow: 135, endColumn: 7, endRow: 135 });
assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 136, endColumn: 17, endRow: 136 });
// Wrap around to first result
assert.deepEqual(await page.evaluate(`window.search.findNext('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 53, endColumn: 30, endRow: 53 });
});
it('should find all occurrences using findPrevious', async () => {
await writeSync(page, fixture);
assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true);
let selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 136, endColumn: 17, endRow: 136 });
assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 1, startRow: 135, endColumn: 7, endRow: 135 });
assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 127, endColumn: 17, endRow: 127 });
assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 1, startRow: 126, endColumn: 7, endRow: 126 });
assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 115, endColumn: 17, endRow: 115 });
assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 1, startRow: 114, endColumn: 7, endRow: 114 });
assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 96, endColumn: 30, endRow: 96 });
assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 76, endColumn: 30, endRow: 76 });
assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 24, startRow: 53, endColumn: 30, endRow: 53 });
// Wrap around to first result
assert.deepEqual(await page.evaluate(`window.search.findPrevious('opencv')`), true);
selectionPosition = await page.evaluate(`window.term.getSelectionPosition()`);
assert.deepEqual(selectionPosition, { startColumn: 11, startRow: 136, endColumn: 17, endRow: 136 });
});
});
});
});
function makeData(length: number): string {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
}
@@ -1,68 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal, ILinkMatcherOptions, IDisposable, ITerminalAddon } from 'xterm';
declare module 'xterm-addon-search' {
/**
* Options for a search.
*/
export interface ISearchOptions {
/**
* Whether the search term is a regex.
*/
regex?: boolean;
/**
* Whether to search for a whole word, the result is only valid if it's
* surrounded in "non-word" characters such as `_`, `(`, `)` or space.
*/
wholeWord?: boolean;
/**
* Whether the search is case sensitive.
*/
caseSensitive?: boolean;
/**
* Whether to do an incremental search, this will expand the selection if it
* still matches the term the user typed. Note that this only affects
* `findNext`, not `findPrevious`.
*/
incremental?: boolean;
}
/**
* An xterm.js addon that provides search functionality.
*/
export class SearchAddon implements ITerminalAddon {
/**
* Activates the addon
* @param terminal The terminal the addon is being loaded in.
*/
public activate(terminal: Terminal): void;
/**
* Disposes the addon.
*/
public dispose(): void;
/**
* Search forwards for the next result that matches the search term and
* options.
* @param term The search term.
* @param searchOptions The options for the search.
*/
public findNext(term: string, searchOptions?: ISearchOptions): boolean;
/**
* Search backwards for the previous result that matches the search term and
* options.
* @param term The search term.
* @param searchOptions The options for the search.
*/
public findPrevious(term: string, searchOptions?: ISearchOptions): boolean;
}
}
@@ -1,31 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
const path = require('path');
const addonName = 'SearchAddon';
const mainFile = 'xterm-addon-search.js';
module.exports = {
entry: `./out/${addonName}.js`,
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre",
exclude: /node_modules/
}
]
},
output: {
filename: mainFile,
path: path.resolve('./lib'),
library: addonName,
libraryTarget: 'umd'
},
mode: 'production'
};
-3
View File
@@ -1,3 +0,0 @@
lib
node_modules
out-benchmark
-5
View File
@@ -1,5 +0,0 @@
**/*.api.js
**/*.api.ts
tsconfig.json
.yarnrc
webpack.config.js
-42
View File
@@ -1,42 +0,0 @@
## xterm-addon-serialize
An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables xterm.js to serialize a terminal framebuffer into string or html. This addon requires xterm.js v4+.
⚠️ This is an experimental addon that is still under construction ⚠️
### Install
```bash
npm install --save xterm-addon-serialize
```
### Usage
```ts
import { Terminal } from "xterm";
import { SerializeAddon } from "xterm-addon-serialize";
const terminal = new Terminal();
const serializeAddon = new SerializeAddon();
terminal.loadAddon(serializeAddon);
terminal.write("something...", () => {
console.log(serializeAddon.serialize());
});
```
See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/xterm-addon-serialize/typings/xterm-addon-serialize.d.ts) for more advanced usage.
### Benchmark
⚠️ Ensure you have `lolcat`, `hexdump` programs installed in your computer
```shell
$ git clone https://github.com/xtermjs/xterm.js.git
$ cd xterm.js
$ yarn
$ cd addons/xterm-addon-serialize
$ yarn benchmark && yarn benchmark-baseline
$ # change some code in `xterm-addon-serialize`
$ yarn benchmark-eval
```
@@ -1,65 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark';
import { spawn } from 'node-pty';
import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder';
import { Terminal } from 'browser/public/Terminal';
import { SerializeAddon } from 'SerializeAddon';
class TestTerminal extends Terminal {
public writeSync(data: string): void {
(this as any)._core.writeSync(data);
}
}
perfContext('Terminal: sh -c "dd if=/dev/random count=40 bs=1k | hexdump | lolcat -f"', () => {
let content = '';
let contentUtf8: Uint8Array;
before(async () => {
const p = spawn('sh', ['-c', 'dd if=/dev/random count=40 bs=1k | hexdump | lolcat -f'], {
name: 'xterm-256color',
cols: 80,
rows: 25,
cwd: process.env.HOME,
env: process.env,
encoding: (null as unknown as string) // needs to be fixed in node-pty
});
const chunks: Buffer[] = [];
let length = 0;
p.on('data', data => {
chunks.push(data as unknown as Buffer);
length += data.length;
});
await new Promise(resolve => p.on('exit', () => resolve()));
contentUtf8 = Buffer.concat(chunks, length);
// translate to content string
const buffer = new Uint32Array(contentUtf8.length);
const decoder = new Utf8ToUtf32();
const codepoints = decoder.decode(contentUtf8, buffer);
for (let i = 0; i < codepoints; ++i) {
content += stringFromCodePoint(buffer[i]);
// peek into content to force flat repr in v8
if (!(i % 10000000)) {
content[i];
}
}
});
perfContext('serialize', () => {
let terminal: TestTerminal;
const serializeAddon = new SerializeAddon();
before(() => {
terminal = new TestTerminal({ cols: 80, rows: 25, scrollback: 5000 });
serializeAddon.activate(terminal);
terminal.writeSync(content);
});
new ThroughputRuntimeCase('', () => {
return { payloadSize: serializeAddon.serialize().length };
}, { fork: false }).showAverageThroughput();
});
});
@@ -1,19 +0,0 @@
{
"APP_PATH": ".benchmark",
"evalConfig": {
"tolerance": {
"*": [0.75, 1.5],
"*.dev": [0.01, 1.5],
"*.cv": [0.01, 1.5],
"EscapeSequenceParser.benchmark.js.*.averageThroughput.mean": [0.9, 5]
},
"skip": [
"*.median",
"*.runs",
"*.dev",
"*.cv",
"EscapeSequenceParser.benchmark.js.*.averageRuntime",
"Terminal.benchmark.js.*.averageRuntime"
]
}
}
@@ -1,23 +0,0 @@
{
"compilerOptions": {
"lib": ["dom", "es6"],
"outDir": "../out-benchmark",
"types": ["../../../node_modules/@types/node"],
"moduleResolution": "node",
"strict": false,
"target": "es2015",
"module": "commonjs",
"baseUrl": ".",
"paths": {
"common/*": ["../../../src/common/*"],
"browser/*": ["../../../src/browser/*"],
"SerializeAddon": ["../src/SerializeAddon"]
}
},
"include": ["../**/*", "../../../typings/xterm.d.ts", "../../../out/**/*"],
"exclude": ["../../../**/*test.ts", "../../**/*api.ts"],
"references": [
{ "path": "../../../src/common" },
{ "path": "../../../src/browser" }
]
}
-23
View File
@@ -1,23 +0,0 @@
{
"name": "xterm-addon-serialize",
"version": "0.3.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
},
"main": "lib/xterm-addon-serialize.js",
"types": "typings/xterm-addon-serialize.d.ts",
"license": "MIT",
"scripts": {
"build": "../../node_modules/.bin/tsc -p src",
"prepackage": "npm run build",
"package": "../../node_modules/.bin/webpack",
"prepublishOnly": "npm run package",
"benchmark": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json",
"benchmark-baseline": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline out-benchmark/addons/xterm-addon-serialize/benchmark/*benchmark.js",
"benchmark-eval": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/addons/xterm-addon-serialize/benchmark/*benchmark.js"
},
"peerDependencies": {
"xterm": "^4.0.0"
}
}
@@ -1,340 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { openTerminal, writeSync, getBrowserType } from '../../../out-test/api/TestUtils';
import { Browser, Page } from 'playwright-core';
const APP = 'http://127.0.0.1:3000/test';
let browser: Browser;
let page: Page;
const width = 800;
const height = 600;
describe('SerializeAddon', () => {
before(async function(): Promise<any> {
const browserType = getBrowserType();
browser = await browserType.launch({ dumpio: true,
headless: process.argv.indexOf('--headless') !== -1
});
page = await (await browser.newContext()).newPage();
await page.setViewportSize({ width, height });
await page.goto(APP);
await openTerminal(page, { rows: 10, cols: 10, rendererType: 'dom' });
await page.evaluate(`
window.serializeAddon = new SerializeAddon();
window.term.loadAddon(window.serializeAddon);
`);
});
after(async () => await browser.close());
beforeEach(async () => await page.evaluate(`window.term.reset()`));
it('empty content', async function(): Promise<any> {
const rows = 10;
const cols = 10;
assert.equal(await page.evaluate(`serializeAddon.serialize();`), '');
});
it('trim last empty lines', async function(): Promise<any> {
const cols = 10;
const lines = [
'',
'',
digitsString(cols),
digitsString(cols),
'',
'',
digitsString(cols),
digitsString(cols),
'',
'',
''
];
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.slice(0, 8).join('\r\n'));
});
it('digits content', async function(): Promise<any> {
const rows = 10;
const cols = 10;
const digitsLine = digitsString(cols);
const lines = newArray<string>(digitsLine, rows);
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
});
it('serialize half rows of content', async function(): Promise<any> {
const rows = 10;
const halfRows = rows >> 1;
const cols = 10;
const lines = newArray<string>((index: number) => digitsString(cols, index), rows);
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize(${halfRows});`), lines.slice(halfRows, 2 * halfRows).join('\r\n'));
});
it('serialize 0 rows of content', async function(): Promise<any> {
const rows = 10;
const cols = 10;
const lines = newArray<string>((index: number) => digitsString(cols, index), rows);
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize(0);`), '');
});
it('serialize all rows of content with color16', async function(): Promise<any> {
const cols = 10;
const color16 = [
30, 31, 32, 33, 34, 35, 36, 37, // Set foreground color
90, 91, 92, 93, 94, 95, 96, 97,
40, 41, 42, 43, 44, 45, 46, 47, // Set background color
100, 101, 103, 104, 105, 106, 107
];
const rows = color16.length;
const lines = newArray<string>(
(index: number) => digitsString(cols, index, `\x1b[${color16[index % color16.length]}m`),
rows
);
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
});
it('serialize all rows of content with fg/bg flags', async function(): Promise<any> {
const cols = 10;
const line = '+'.repeat(cols);
const lines: string[] = [
mkSGR(FG_P16_GREEN) + line, // Workaround: If we clear all flags a the end, serialize will use \x1b[0m to clear instead of the sepcific disable sequence
mkSGR(INVERSE) + line,
mkSGR(BOLD) + line,
mkSGR(UNDERLINED) + line,
mkSGR(BLINK) + line,
mkSGR(INVISIBLE) + line,
mkSGR(NO_INVERSE) + line,
mkSGR(NO_BOLD) + line,
mkSGR(NO_UNDERLINED) + line,
mkSGR(NO_BLINK) + line,
mkSGR(NO_INVISIBLE) + line
];
const rows = lines.length;
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
});
it('serialize all rows of content with color256', async function(): Promise<any> {
const rows = 32;
const cols = 10;
const lines = newArray<string>(
(index: number) => digitsString(cols, index, `\x1b[38;5;${16 + index}m`),
rows
);
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
});
it('serialize all rows of content with color16 and style separately', async function(): Promise<any> {
const cols = 10;
const line = '+'.repeat(cols);
const lines: string[] = [
mkSGR(FG_P16_RED) + line, // fg Red,
mkSGR(UNDERLINED) + line, // fg Red, Underlined
mkSGR(FG_P16_GREEN) + line, // fg Green, Underlined
mkSGR(INVERSE) + line, // fg Green, Underlined, Inverse
mkSGR(NO_INVERSE) + line, // fg Green, Underlined
mkSGR(INVERSE) + line, // fg Green, Underlined, Inverse
mkSGR(BG_P16_YELLOW) + line, // fg Green, bg Yellow, Underlined, Inverse
mkSGR(FG_RESET) + line, // bg Yellow, Underlined, Inverse
mkSGR(BG_RESET) + line, // Underlined, Inverse
mkSGR(NORMAL) + line // Back to normal
];
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
});
it('serialize all rows of content with color16 and style together', async function(): Promise<any> {
const cols = 10;
const line = '+'.repeat(cols);
const lines: string[] = [
mkSGR(FG_P16_RED) + line, // fg Red
mkSGR(FG_P16_GREEN, BG_P16_YELLOW) + line, // fg Green, bg Yellow
mkSGR(UNDERLINED, ITALIC) + line, // fg Green, bg Yellow, Underlined, Italic
mkSGR(NO_UNDERLINED, NO_ITALIC) + line, // fg Green, bg Yellow
mkSGR(FG_RESET, ITALIC) + line, // bg Yellow, Italic
mkSGR(BG_RESET) + line, // Italic
mkSGR(NORMAL) + line, // Back to normal
mkSGR(FG_P16_RED) + line, // fg Red
mkSGR(FG_P16_GREEN, BG_P16_YELLOW) + line, // fg Green, bg Yellow
mkSGR(UNDERLINED, ITALIC) + line, // fg Green, bg Yellow, Underlined, Italic
mkSGR(NO_UNDERLINED, NO_ITALIC) + line, // fg Green, bg Yellow
mkSGR(FG_RESET, ITALIC) + line, // bg Yellow, Italic
mkSGR(BG_RESET) + line // Italic
];
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
});
it('serialize all rows of content with color256 and style separately', async function(): Promise<any> {
const cols = 10;
const line = '+'.repeat(cols);
const lines: string[] = [
mkSGR(FG_P256_RED) + line, // fg Red 256,
mkSGR(UNDERLINED) + line, // fg Red 256, Underlined
mkSGR(FG_P256_GREEN) + line, // fg Green 256, Underlined
mkSGR(INVERSE) + line, // fg Green 256, Underlined, Inverse
mkSGR(NO_INVERSE) + line, // fg Green 256, Underlined
mkSGR(INVERSE) + line, // fg Green 256, Underlined, Inverse
mkSGR(BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256, Underlined, Inverse
mkSGR(FG_RESET) + line, // bg Yellow 256, Underlined, Inverse
mkSGR(BG_RESET) + line, // Underlined, Inverse
mkSGR(NORMAL) + line // Back to normal
];
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
});
it('serialize all rows of content with color256 and style together', async function(): Promise<any> {
const cols = 10;
const line = '+'.repeat(cols);
const lines: string[] = [
mkSGR(FG_P256_RED) + line, // fg Red 256
mkSGR(FG_P256_GREEN, BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256
mkSGR(UNDERLINED, ITALIC) + line, // fg Green 256, bg Yellow 256, Underlined, Italic
mkSGR(NO_UNDERLINED, NO_ITALIC) + line, // fg Green 256, bg Yellow 256
mkSGR(FG_RESET, ITALIC) + line, // bg Yellow 256, Italic
mkSGR(BG_RESET) + line, // Italic
mkSGR(NORMAL) + line, // Back to normal
mkSGR(FG_P256_RED) + line, // fg Red 256
mkSGR(FG_P256_GREEN, BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256
mkSGR(UNDERLINED, ITALIC) + line, // fg Green 256, bg Yellow 256, Underlined, Italic
mkSGR(NO_UNDERLINED, NO_ITALIC) + line, // fg Green 256, bg Yellow 256
mkSGR(FG_RESET, ITALIC) + line, // bg Yellow 256, Italic
mkSGR(BG_RESET) + line // Italic
];
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
});
it('serialize all rows of content with colorRGB and style separately', async function(): Promise<any> {
const cols = 10;
const line = '+'.repeat(cols);
const lines: string[] = [
mkSGR(FG_RGB_RED) + line, // fg Red RGB,
mkSGR(UNDERLINED) + line, // fg Red RGB, Underlined
mkSGR(FG_RGB_GREEN) + line, // fg Green RGB, Underlined
mkSGR(INVERSE) + line, // fg Green RGB, Underlined, Inverse
mkSGR(NO_INVERSE) + line, // fg Green RGB, Underlined
mkSGR(INVERSE) + line, // fg Green RGB, Underlined, Inverse
mkSGR(BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB, Underlined, Inverse
mkSGR(FG_RESET) + line, // bg Yellow RGB, Underlined, Inverse
mkSGR(BG_RESET) + line, // Underlined, Inverse
mkSGR(NORMAL) + line // Back to normal
];
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
});
it('serialize all rows of content with colorRGB and style together', async function(): Promise<any> {
const cols = 10;
const line = '+'.repeat(cols);
const lines: string[] = [
mkSGR(FG_RGB_RED) + line, // fg Red RGB
mkSGR(FG_RGB_GREEN, BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB
mkSGR(UNDERLINED, ITALIC) + line, // fg Green RGB, bg Yellow RGB, Underlined, Italic
mkSGR(NO_UNDERLINED, NO_ITALIC) + line, // fg Green RGB, bg Yellow RGB
mkSGR(FG_RESET, ITALIC) + line, // bg Yellow RGB, Italic
mkSGR(BG_RESET) + line, // Italic
mkSGR(NORMAL) + line, // Back to normal
mkSGR(FG_RGB_RED) + line, // fg Red RGB
mkSGR(FG_RGB_GREEN, BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB
mkSGR(UNDERLINED, ITALIC) + line, // fg Green RGB, bg Yellow RGB, Underlined, Italic
mkSGR(NO_UNDERLINED, NO_ITALIC) + line, // fg Green RGB, bg Yellow RGB
mkSGR(FG_RESET, ITALIC) + line, // bg Yellow RGB, Italic
mkSGR(BG_RESET) + line // Italic
];
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n'));
});
it('serialize tabs correctly', async () => {
const lines = [
'a\tb',
'aa\tc',
'aaa\td'
];
const expected = [
'a\x1b[7Cb',
'aa\x1b[6Cc',
'aaa\x1b[5Cd'
];
await writeSync(page, lines.join('\\r\\n'));
assert.equal(await page.evaluate(`serializeAddon.serialize();`), expected.join('\r\n'));
});
});
function newArray<T>(initial: T | ((index: number) => T), count: number): T[] {
const array: T[] = new Array<T>(count);
for (let i = 0; i < array.length; i++) {
if (typeof initial === 'function') {
array[i] = (<(index: number) => T>initial)(i);
} else {
array[i] = <T>initial;
}
}
return array;
}
function digitsString(length: number, from: number = 0, sgr: string = ''): string {
let s = sgr;
for (let i = 0; i < length; i++) {
s += `${(from++) % 10}`;
}
return s;
}
function mkSGR(...seq: string[]): string {
return `\x1b[${seq.join(';')}m`;
}
const NORMAL = '0';
const FG_P16_RED = '31';
const FG_P16_GREEN = '32';
const FG_P16_YELLOW = '33';
const FG_P256_RED = '38;5;196';
const FG_P256_GREEN = '38;5;46';
const FG_P256_YELLOW = '38;5;226';
const FG_RGB_RED = '38;2;255;0;0';
const FG_RGB_GREEN = '38;2;0;255;0';
const FG_RGB_YELLOW = '38;2;255;255;0';
const FG_RESET = '39';
const BG_P16_RED = '41';
const BG_P16_GREEN = '42';
const BG_P16_YELLOW = '43';
const BG_P256_RED = '48;5;196';
const BG_P256_GREEN = '48;5;46';
const BG_P256_YELLOW = '48;5;226';
const BG_RGB_RED = '48;2;255;0;0';
const BG_RGB_GREEN = '48;2;0;255;0';
const BG_RGB_YELLOW = '48;2;255;255;0';
const BG_RESET = '49';
const INVERSE = '7';
const BOLD = '1';
const UNDERLINED = '4';
const BLINK = '5';
const INVISIBLE = '8';
const NO_INVERSE = '27';
const NO_BOLD = '22';
const NO_UNDERLINED = '24';
const NO_BLINK = '25';
const NO_INVISIBLE = '28';
const ITALIC = '3';
const DIM = '2';
const NO_ITALIC = '23';
const NO_DIM = '22';
@@ -1,187 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*
* (EXPERIMENTAL) This Addon is still under development
*/
import { Terminal, ITerminalAddon, IBuffer, IBufferCell } from 'xterm';
function constrain(value: number, low: number, high: number): number {
return Math.max(low, Math.min(value, high));
}
// TODO: Refine this template class later
abstract class BaseSerializeHandler {
constructor(private _buffer: IBuffer) { }
public serialize(startRow: number, endRow: number): string {
// we need two of them to flip between old and new cell
const cell1 = this._buffer.getNullCell();
const cell2 = this._buffer.getNullCell();
let oldCell = cell1;
this._beforeSerialize(endRow - startRow);
for (let row = startRow; row < endRow; row++) {
const line = this._buffer.getLine(row);
if (line) {
for (let col = 0; col < line.length; col++) {
const c = line.getCell(col, oldCell === cell1 ? cell2 : cell1);
if (!c) {
console.warn(`Can't get cell at row=${row}, col=${col}`);
continue;
}
this._nextCell(c, oldCell, row, col);
oldCell = c;
}
}
this._rowEnd(row);
}
this._afterSerialize();
return this._serializeString();
}
protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { }
protected _rowEnd(row: number): void { }
protected _beforeSerialize(rows: number): void { }
protected _afterSerialize(): void { }
protected _serializeString(): string { return ''; }
}
function equalFg(cell1: IBufferCell, cell2: IBufferCell): boolean {
return cell1.getFgColorMode() === cell2.getFgColorMode()
&& cell1.getFgColor() === cell2.getFgColor();
}
function equalBg(cell1: IBufferCell, cell2: IBufferCell): boolean {
return cell1.getBgColorMode() === cell2.getBgColorMode()
&& cell1.getBgColor() === cell2.getBgColor();
}
function equalFlags(cell1: IBufferCell, cell2: IBufferCell): boolean {
return cell1.isInverse() === cell2.isInverse()
&& cell1.isBold() === cell2.isBold()
&& cell1.isUnderline() === cell2.isUnderline()
&& cell1.isBlink() === cell2.isBlink()
&& cell1.isInvisible() === cell2.isInvisible()
&& cell1.isItalic() === cell2.isItalic()
&& cell1.isDim() === cell2.isDim();
}
class StringSerializeHandler extends BaseSerializeHandler {
private _rowIndex: number = 0;
private _allRows: string[] = new Array<string>();
private _currentRow: string = '';
private _nullCellCount: number = 0;
constructor(buffer: IBuffer) {
super(buffer);
}
protected _beforeSerialize(rows: number): void {
this._allRows = new Array<string>(rows);
}
protected _rowEnd(row: number): void {
this._allRows[this._rowIndex++] = this._currentRow;
this._currentRow = '';
this._nullCellCount = 0;
}
protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void {
const sgrSeq: number[] = [];
const fgChanged = !equalFg(cell, oldCell);
const bgChanged = !equalBg(cell, oldCell);
const flagsChanged = !equalFlags(cell, oldCell);
if (fgChanged || bgChanged || flagsChanged) {
if (cell.isAttributeDefault()) {
this._currentRow += '\x1b[0m';
} else {
if (fgChanged) {
const color = cell.getFgColor();
if (cell.isFgRGB()) { sgrSeq.push(38, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF); }
else if (cell.isFgPalette()) {
if (color >= 16) { sgrSeq.push(38, 5, color); }
else { sgrSeq.push(color & 8 ? 90 + (color & 7) : 30 + (color & 7)); }
}
else { sgrSeq.push(39); }
}
if (bgChanged) {
const color = cell.getBgColor();
if (cell.isBgRGB()) { sgrSeq.push(48, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF); }
else if (cell.isBgPalette()) {
if (color >= 16) { sgrSeq.push(48, 5, color); }
else { sgrSeq.push(color & 8 ? 100 + (color & 7) : 40 + (color & 7)); }
}
else { sgrSeq.push(49); }
}
if (flagsChanged) {
if (cell.isInverse() !== oldCell.isInverse()) { sgrSeq.push(cell.isInverse() ? 7 : 27); }
if (cell.isBold() !== oldCell.isBold()) { sgrSeq.push(cell.isBold() ? 1 : 22); }
if (cell.isUnderline() !== oldCell.isUnderline()) { sgrSeq.push(cell.isUnderline() ? 4 : 24); }
if (cell.isBlink() !== oldCell.isBlink()) { sgrSeq.push(cell.isBlink() ? 5 : 25); }
if (cell.isInvisible() !== oldCell.isInvisible()) { sgrSeq.push(cell.isInvisible() ? 8 : 28); }
if (cell.isItalic() !== oldCell.isItalic()) { sgrSeq.push(cell.isItalic() ? 3 : 23); }
if (cell.isDim() !== oldCell.isDim()) { sgrSeq.push(cell.isDim() ? 2 : 22); }
}
}
}
if (sgrSeq.length) {
this._currentRow += `\x1b[${sgrSeq.join(';')}m`;
}
// Count number of null cells encountered after the last non-null cell and move the cursor
// if a non-null cell is found (eg. \t or cursor move)
if (cell.getChars() === '') {
this._nullCellCount++;
} else if (this._nullCellCount > 0) {
this._currentRow += `\x1b[${this._nullCellCount}C`;
this._nullCellCount = 0;
}
this._currentRow += cell.getChars();
}
protected _serializeString(): string {
let rowEnd = this._allRows.length;
for (; rowEnd > 0; rowEnd--) {
if (this._allRows[rowEnd - 1]) {
break;
}
}
return this._allRows.slice(0, rowEnd).join('\r\n');
}
}
export class SerializeAddon implements ITerminalAddon {
private _terminal: Terminal | undefined;
constructor() { }
public activate(terminal: Terminal): void {
this._terminal = terminal;
}
public serialize(rows?: number): string {
// TODO: Add re-position cursor support
// TODO: Add word wrap mode support
// TODO: Add combinedData support
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
const maxRows = this._terminal.buffer.active.length;
const handler = new StringSerializeHandler(this._terminal.buffer.active);
rows = (rows === undefined) ? maxRows : constrain(rows, 0, maxRows);
return handler.serialize(maxRows - rows, maxRows);
}
public dispose(): void { }
}
@@ -1,34 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"lib": [
"dom",
"es2015"
],
"rootDir": ".",
"outDir": "../out",
"sourceMap": true,
"removeComments": true,
"baseUrl": ".",
"paths": {
"common/*": [
"../../../src/common/*"
]
},
"strict": true,
"types": [
"../../../node_modules/@types/mocha",
"../../../out-test/api/TestUtils"
]
},
"include": [
"./**/*",
"../../../typings/xterm.d.ts"
],
"references": [
{
"path": "../../../src/common"
}
]
}
@@ -1,38 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal, ITerminalAddon } from 'xterm';
declare module 'xterm-addon-serialize' {
/**
* An xterm.js addon that enables web links.
*/
export class SerializeAddon implements ITerminalAddon {
constructor();
/**
* Activates the addon
* @param terminal The terminal the addon is being loaded in.
*/
public activate(terminal: Terminal): void;
/**
* Serializes terminal rows into a string that can be written back to the terminal
* to restore the state. The cursor will also be positioned to the correct cell.
* When restoring a terminal it is best to do before `Terminal.open` is called
* to avoid wasting CPU cycles rendering incomplete frames.
* @param rows The number of rows to serialize, starting from the bottom of the
* terminal. This defaults to the number of rows in the viewport.
*/
public serialize(rows?: number): string;
/**
* Disposes the addon.
*/
public dispose(): void;
}
}
@@ -1,31 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
const path = require('path');
const addonName = 'SerializeAddon';
const mainFile = 'xterm-addon-serialize.js';
module.exports = {
entry: `./out/${addonName}.js`,
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre",
exclude: /node_modules/
}
]
},
output: {
filename: mainFile,
path: path.resolve('./lib'),
library: addonName,
libraryTarget: 'umd'
},
mode: 'production'
};
-2
View File
@@ -1,2 +0,0 @@
lib
node_modules
-5
View File
@@ -1,5 +0,0 @@
**/*.api.js
**/*.api.ts
tsconfig.json
.yarnrc
webpack.config.js
-19
View File
@@ -1,19 +0,0 @@
Copyright (c) 2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-23
View File
@@ -1,23 +0,0 @@
## xterm-addon-unicode11
An addon providing Unicode version 11 rules for xterm.js.
### Install
```bash
npm install --save xterm-addon-unicode11
```
### Usage
```ts
import { Terminal } from 'xterm';
import { Unicode11Addon } from 'xterm-addon-unicode11';
const terminal = new Terminal();
const unicode11Addon = new Unicode11Addon();
terminal.loadAddon(unicode11Addon);
// activate the new version
terminal.unicode.activeVersion = '11';
```
-21
View File
@@ -1,21 +0,0 @@
{
"name": "xterm-addon-unicode11",
"version": "0.2.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
},
"main": "lib/xterm-addon-unicode11.js",
"types": "typings/xterm-addon-unicode11.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"scripts": {
"build": "../../node_modules/.bin/tsc -p src",
"prepackage": "npm run build",
"package": "../../node_modules/.bin/webpack",
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^4.0.0"
}
}
@@ -1,49 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { openTerminal, getBrowserType } from '../../../out-test/api/TestUtils';
import { Browser, Page } from 'playwright-core';
const APP = 'http://127.0.0.1:3000/test';
let browser: Browser;
let page: Page;
const width = 800;
const height = 600;
describe('Unicode11Addon', () => {
before(async function(): Promise<any> {
const browserType = getBrowserType();
browser = await browserType.launch({ dumpio: true,
headless: process.argv.indexOf('--headless') !== -1
});
page = await (await browser.newContext()).newPage();
await page.setViewportSize({ width, height });
});
after(async () => {
await browser.close();
});
beforeEach(async function(): Promise<any> {
await page.goto(APP);
await openTerminal(page);
});
it('wcwidth V11 emoji test', async () => {
await page.evaluate(`
window.unicode11 = new Unicode11Addon();
window.term.loadAddon(window.unicode11);
`);
// should have loaded '11'
assert.deepEqual(await page.evaluate(`window.term.unicode.versions`), ['6', '11']);
// switch should not throw
await page.evaluate(`window.term.unicode.activeVersion = '11';`);
assert.deepEqual(await page.evaluate(`window.term.unicode.activeVersion`), '11');
// v6: 10, V11: 20
assert.deepEqual(await page.evaluate(`window.term._core.unicodeService.getStringCellWidth('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣')`), 20);
});
});
@@ -1,16 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*
* UnicodeVersionProvider for V11.
*/
import { Terminal, ITerminalAddon } from 'xterm';
import { UnicodeV11 } from './UnicodeV11';
export class Unicode11Addon implements ITerminalAddon {
public activate(terminal: Terminal): void {
terminal.unicode.register(new UnicodeV11());
}
public dispose(): void { }
}
@@ -1,222 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IUnicodeVersionProvider } from 'xterm';
import { fill } from 'common/TypedArrayUtils';
type CharWidth = 0 | 1 | 2;
const BMP_COMBINING = [
[0x0300, 0x036F], [0x0483, 0x0489], [0x0591, 0x05BD],
[0x05BF, 0x05BF], [0x05C1, 0x05C2], [0x05C4, 0x05C5],
[0x05C7, 0x05C7], [0x0600, 0x0605], [0x0610, 0x061A],
[0x061C, 0x061C], [0x064B, 0x065F], [0x0670, 0x0670],
[0x06D6, 0x06DD], [0x06DF, 0x06E4], [0x06E7, 0x06E8],
[0x06EA, 0x06ED], [0x070F, 0x070F], [0x0711, 0x0711],
[0x0730, 0x074A], [0x07A6, 0x07B0], [0x07EB, 0x07F3],
[0x07FD, 0x07FD], [0x0816, 0x0819], [0x081B, 0x0823],
[0x0825, 0x0827], [0x0829, 0x082D], [0x0859, 0x085B],
[0x08D3, 0x0902], [0x093A, 0x093A], [0x093C, 0x093C],
[0x0941, 0x0948], [0x094D, 0x094D], [0x0951, 0x0957],
[0x0962, 0x0963], [0x0981, 0x0981], [0x09BC, 0x09BC],
[0x09C1, 0x09C4], [0x09CD, 0x09CD], [0x09E2, 0x09E3],
[0x09FE, 0x09FE], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C],
[0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D],
[0x0A51, 0x0A51], [0x0A70, 0x0A71], [0x0A75, 0x0A75],
[0x0A81, 0x0A82], [0x0ABC, 0x0ABC], [0x0AC1, 0x0AC5],
[0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], [0x0AE2, 0x0AE3],
[0x0AFA, 0x0AFF], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C],
[0x0B3F, 0x0B3F], [0x0B41, 0x0B44], [0x0B4D, 0x0B4D],
[0x0B56, 0x0B56], [0x0B62, 0x0B63], [0x0B82, 0x0B82],
[0x0BC0, 0x0BC0], [0x0BCD, 0x0BCD], [0x0C00, 0x0C00],
[0x0C04, 0x0C04], [0x0C3E, 0x0C40], [0x0C46, 0x0C48],
[0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0C62, 0x0C63],
[0x0C81, 0x0C81], [0x0CBC, 0x0CBC], [0x0CBF, 0x0CBF],
[0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], [0x0CE2, 0x0CE3],
[0x0D00, 0x0D01], [0x0D3B, 0x0D3C], [0x0D41, 0x0D44],
[0x0D4D, 0x0D4D], [0x0D62, 0x0D63], [0x0DCA, 0x0DCA],
[0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], [0x0E31, 0x0E31],
[0x0E34, 0x0E3A], [0x0E47, 0x0E4E], [0x0EB1, 0x0EB1],
[0x0EB4, 0x0EBC], [0x0EC8, 0x0ECD], [0x0F18, 0x0F19],
[0x0F35, 0x0F35], [0x0F37, 0x0F37], [0x0F39, 0x0F39],
[0x0F71, 0x0F7E], [0x0F80, 0x0F84], [0x0F86, 0x0F87],
[0x0F8D, 0x0F97], [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6],
[0x102D, 0x1030], [0x1032, 0x1037], [0x1039, 0x103A],
[0x103D, 0x103E], [0x1058, 0x1059], [0x105E, 0x1060],
[0x1071, 0x1074], [0x1082, 0x1082], [0x1085, 0x1086],
[0x108D, 0x108D], [0x109D, 0x109D], [0x1160, 0x11FF],
[0x135D, 0x135F], [0x1712, 0x1714], [0x1732, 0x1734],
[0x1752, 0x1753], [0x1772, 0x1773], [0x17B4, 0x17B5],
[0x17B7, 0x17BD], [0x17C6, 0x17C6], [0x17C9, 0x17D3],
[0x17DD, 0x17DD], [0x180B, 0x180E], [0x1885, 0x1886],
[0x18A9, 0x18A9], [0x1920, 0x1922], [0x1927, 0x1928],
[0x1932, 0x1932], [0x1939, 0x193B], [0x1A17, 0x1A18],
[0x1A1B, 0x1A1B], [0x1A56, 0x1A56], [0x1A58, 0x1A5E],
[0x1A60, 0x1A60], [0x1A62, 0x1A62], [0x1A65, 0x1A6C],
[0x1A73, 0x1A7C], [0x1A7F, 0x1A7F], [0x1AB0, 0x1ABE],
[0x1B00, 0x1B03], [0x1B34, 0x1B34], [0x1B36, 0x1B3A],
[0x1B3C, 0x1B3C], [0x1B42, 0x1B42], [0x1B6B, 0x1B73],
[0x1B80, 0x1B81], [0x1BA2, 0x1BA5], [0x1BA8, 0x1BA9],
[0x1BAB, 0x1BAD], [0x1BE6, 0x1BE6], [0x1BE8, 0x1BE9],
[0x1BED, 0x1BED], [0x1BEF, 0x1BF1], [0x1C2C, 0x1C33],
[0x1C36, 0x1C37], [0x1CD0, 0x1CD2], [0x1CD4, 0x1CE0],
[0x1CE2, 0x1CE8], [0x1CED, 0x1CED], [0x1CF4, 0x1CF4],
[0x1CF8, 0x1CF9], [0x1DC0, 0x1DF9], [0x1DFB, 0x1DFF],
[0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2064],
[0x2066, 0x206F], [0x20D0, 0x20F0], [0x2CEF, 0x2CF1],
[0x2D7F, 0x2D7F], [0x2DE0, 0x2DFF], [0x302A, 0x302D],
[0x3099, 0x309A], [0xA66F, 0xA672], [0xA674, 0xA67D],
[0xA69E, 0xA69F], [0xA6F0, 0xA6F1], [0xA802, 0xA802],
[0xA806, 0xA806], [0xA80B, 0xA80B], [0xA825, 0xA826],
[0xA8C4, 0xA8C5], [0xA8E0, 0xA8F1], [0xA8FF, 0xA8FF],
[0xA926, 0xA92D], [0xA947, 0xA951], [0xA980, 0xA982],
[0xA9B3, 0xA9B3], [0xA9B6, 0xA9B9], [0xA9BC, 0xA9BD],
[0xA9E5, 0xA9E5], [0xAA29, 0xAA2E], [0xAA31, 0xAA32],
[0xAA35, 0xAA36], [0xAA43, 0xAA43], [0xAA4C, 0xAA4C],
[0xAA7C, 0xAA7C], [0xAAB0, 0xAAB0], [0xAAB2, 0xAAB4],
[0xAAB7, 0xAAB8], [0xAABE, 0xAABF], [0xAAC1, 0xAAC1],
[0xAAEC, 0xAAED], [0xAAF6, 0xAAF6], [0xABE5, 0xABE5],
[0xABE8, 0xABE8], [0xABED, 0xABED], [0xFB1E, 0xFB1E],
[0xFE00, 0xFE0F], [0xFE20, 0xFE2F], [0xFEFF, 0xFEFF],
[0xFFF9, 0xFFFB]
];
const HIGH_COMBINING = [
[0x101FD, 0x101FD], [0x102E0, 0x102E0],
[0x10376, 0x1037A], [0x10A01, 0x10A03], [0x10A05, 0x10A06],
[0x10A0C, 0x10A0F], [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F],
[0x10AE5, 0x10AE6], [0x10D24, 0x10D27], [0x10F46, 0x10F50],
[0x11001, 0x11001], [0x11038, 0x11046], [0x1107F, 0x11081],
[0x110B3, 0x110B6], [0x110B9, 0x110BA], [0x110BD, 0x110BD],
[0x110CD, 0x110CD], [0x11100, 0x11102], [0x11127, 0x1112B],
[0x1112D, 0x11134], [0x11173, 0x11173], [0x11180, 0x11181],
[0x111B6, 0x111BE], [0x111C9, 0x111CC], [0x1122F, 0x11231],
[0x11234, 0x11234], [0x11236, 0x11237], [0x1123E, 0x1123E],
[0x112DF, 0x112DF], [0x112E3, 0x112EA], [0x11300, 0x11301],
[0x1133B, 0x1133C], [0x11340, 0x11340], [0x11366, 0x1136C],
[0x11370, 0x11374], [0x11438, 0x1143F], [0x11442, 0x11444],
[0x11446, 0x11446], [0x1145E, 0x1145E], [0x114B3, 0x114B8],
[0x114BA, 0x114BA], [0x114BF, 0x114C0], [0x114C2, 0x114C3],
[0x115B2, 0x115B5], [0x115BC, 0x115BD], [0x115BF, 0x115C0],
[0x115DC, 0x115DD], [0x11633, 0x1163A], [0x1163D, 0x1163D],
[0x1163F, 0x11640], [0x116AB, 0x116AB], [0x116AD, 0x116AD],
[0x116B0, 0x116B5], [0x116B7, 0x116B7], [0x1171D, 0x1171F],
[0x11722, 0x11725], [0x11727, 0x1172B], [0x1182F, 0x11837],
[0x11839, 0x1183A], [0x119D4, 0x119D7], [0x119DA, 0x119DB],
[0x119E0, 0x119E0], [0x11A01, 0x11A0A], [0x11A33, 0x11A38],
[0x11A3B, 0x11A3E], [0x11A47, 0x11A47], [0x11A51, 0x11A56],
[0x11A59, 0x11A5B], [0x11A8A, 0x11A96], [0x11A98, 0x11A99],
[0x11C30, 0x11C36], [0x11C38, 0x11C3D], [0x11C3F, 0x11C3F],
[0x11C92, 0x11CA7], [0x11CAA, 0x11CB0], [0x11CB2, 0x11CB3],
[0x11CB5, 0x11CB6], [0x11D31, 0x11D36], [0x11D3A, 0x11D3A],
[0x11D3C, 0x11D3D], [0x11D3F, 0x11D45], [0x11D47, 0x11D47],
[0x11D90, 0x11D91], [0x11D95, 0x11D95], [0x11D97, 0x11D97],
[0x11EF3, 0x11EF4], [0x13430, 0x13438], [0x16AF0, 0x16AF4],
[0x16B30, 0x16B36], [0x16F4F, 0x16F4F], [0x16F8F, 0x16F92],
[0x1BC9D, 0x1BC9E], [0x1BCA0, 0x1BCA3], [0x1D167, 0x1D169],
[0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD],
[0x1D242, 0x1D244], [0x1DA00, 0x1DA36], [0x1DA3B, 0x1DA6C],
[0x1DA75, 0x1DA75], [0x1DA84, 0x1DA84], [0x1DA9B, 0x1DA9F],
[0x1DAA1, 0x1DAAF], [0x1E000, 0x1E006], [0x1E008, 0x1E018],
[0x1E01B, 0x1E021], [0x1E023, 0x1E024], [0x1E026, 0x1E02A],
[0x1E130, 0x1E136], [0x1E2EC, 0x1E2EF], [0x1E8D0, 0x1E8D6],
[0x1E944, 0x1E94A], [0xE0001, 0xE0001], [0xE0020, 0xE007F],
[0xE0100, 0xE01EF]
];
const BMP_WIDE = [
[0x1100, 0x115F], [0x231A, 0x231B], [0x2329, 0x232A],
[0x23E9, 0x23EC], [0x23F0, 0x23F0], [0x23F3, 0x23F3],
[0x25FD, 0x25FE], [0x2614, 0x2615], [0x2648, 0x2653],
[0x267F, 0x267F], [0x2693, 0x2693], [0x26A1, 0x26A1],
[0x26AA, 0x26AB], [0x26BD, 0x26BE], [0x26C4, 0x26C5],
[0x26CE, 0x26CE], [0x26D4, 0x26D4], [0x26EA, 0x26EA],
[0x26F2, 0x26F3], [0x26F5, 0x26F5], [0x26FA, 0x26FA],
[0x26FD, 0x26FD], [0x2705, 0x2705], [0x270A, 0x270B],
[0x2728, 0x2728], [0x274C, 0x274C], [0x274E, 0x274E],
[0x2753, 0x2755], [0x2757, 0x2757], [0x2795, 0x2797],
[0x27B0, 0x27B0], [0x27BF, 0x27BF], [0x2B1B, 0x2B1C],
[0x2B50, 0x2B50], [0x2B55, 0x2B55], [0x2E80, 0x2E99],
[0x2E9B, 0x2EF3], [0x2F00, 0x2FD5], [0x2FF0, 0x2FFB],
[0x3000, 0x3029], [0x302E, 0x303E], [0x3041, 0x3096],
[0x309B, 0x30FF], [0x3105, 0x312F], [0x3131, 0x318E],
[0x3190, 0x31BA], [0x31C0, 0x31E3], [0x31F0, 0x321E],
[0x3220, 0x3247], [0x3250, 0x4DBF], [0x4E00, 0xA48C],
[0xA490, 0xA4C6], [0xA960, 0xA97C], [0xAC00, 0xD7A3],
[0xF900, 0xFAFF], [0xFE10, 0xFE19], [0xFE30, 0xFE52],
[0xFE54, 0xFE66], [0xFE68, 0xFE6B], [0xFF01, 0xFF60],
[0xFFE0, 0xFFE6]
];
const HIGH_WIDE = [
[0x16FE0, 0x16FE3], [0x17000, 0x187F7],
[0x18800, 0x18AF2], [0x1B000, 0x1B11E], [0x1B150, 0x1B152],
[0x1B164, 0x1B167], [0x1B170, 0x1B2FB], [0x1F004, 0x1F004],
[0x1F0CF, 0x1F0CF], [0x1F18E, 0x1F18E], [0x1F191, 0x1F19A],
[0x1F200, 0x1F202], [0x1F210, 0x1F23B], [0x1F240, 0x1F248],
[0x1F250, 0x1F251], [0x1F260, 0x1F265], [0x1F300, 0x1F320],
[0x1F32D, 0x1F335], [0x1F337, 0x1F37C], [0x1F37E, 0x1F393],
[0x1F3A0, 0x1F3CA], [0x1F3CF, 0x1F3D3], [0x1F3E0, 0x1F3F0],
[0x1F3F4, 0x1F3F4], [0x1F3F8, 0x1F43E], [0x1F440, 0x1F440],
[0x1F442, 0x1F4FC], [0x1F4FF, 0x1F53D], [0x1F54B, 0x1F54E],
[0x1F550, 0x1F567], [0x1F57A, 0x1F57A], [0x1F595, 0x1F596],
[0x1F5A4, 0x1F5A4], [0x1F5FB, 0x1F64F], [0x1F680, 0x1F6C5],
[0x1F6CC, 0x1F6CC], [0x1F6D0, 0x1F6D2], [0x1F6D5, 0x1F6D5],
[0x1F6EB, 0x1F6EC], [0x1F6F4, 0x1F6FA], [0x1F7E0, 0x1F7EB],
[0x1F90D, 0x1F971], [0x1F973, 0x1F976], [0x1F97A, 0x1F9A2],
[0x1F9A5, 0x1F9AA], [0x1F9AE, 0x1F9CA], [0x1F9CD, 0x1F9FF],
[0x1FA70, 0x1FA73], [0x1FA78, 0x1FA7A], [0x1FA80, 0x1FA82],
[0x1FA90, 0x1FA95], [0x20000, 0x2FFFD], [0x30000, 0x3FFFD]
];
// BMP lookup table, lazy initialized during first addon loading
let table: Uint8Array;
function bisearch(ucs: number, data: number[][]): boolean {
let min = 0;
let max = data.length - 1;
let mid;
if (ucs < data[0][0] || ucs > data[max][1]) {
return false;
}
while (max >= min) {
mid = (min + max) >> 1;
if (ucs > data[mid][1]) {
min = mid + 1;
} else if (ucs < data[mid][0]) {
max = mid - 1;
} else {
return true;
}
}
return false;
}
export class UnicodeV11 implements IUnicodeVersionProvider {
public readonly version = '11';
constructor() {
if (!table) {
table = new Uint8Array(65536);
fill(table, 1);
table[0] = 0;
fill(table, 0, 1, 32);
fill(table, 0, 0x7f, 0xa0);
for (let r = 0; r < BMP_COMBINING.length; ++r) {
fill(table, 0, BMP_COMBINING[r][0], BMP_COMBINING[r][1] + 1);
}
for (let r = 0; r < BMP_WIDE.length; ++r) {
fill(table, 2, BMP_WIDE[r][0], BMP_WIDE[r][1] + 1);
}
}
}
public wcwidth(num: number): CharWidth {
if (num < 32) return 0;
if (num < 127) return 1;
if (num < 65536) return table[num] as CharWidth;
if (bisearch(num, HIGH_COMBINING)) return 0;
if (bisearch(num, HIGH_WIDE)) return 2;
return 1;
}
}
@@ -1,34 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2015",
"lib": [
"dom",
"es2015"
],
"rootDir": ".",
"outDir": "../out",
"sourceMap": true,
"removeComments": true,
"strict": true,
"baseUrl": ".",
"paths": {
"common/*": [
"../../../src/common/*"
]
},
"types": [
"../../../node_modules/@types/mocha",
"../../../out-test/api/TestUtils"
]
},
"include": [
"./**/*",
"../../../typings/xterm.d.ts"
],
"references": [
{
"path": "../../../src/common"
}
]
}
@@ -1,14 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal, ITerminalAddon } from 'xterm';
declare module 'xterm-addon-unicode11' {
export class Unicode11Addon implements ITerminalAddon {
constructor();
public activate(terminal: Terminal): void;
public dispose(): void;
}
}
@@ -1,38 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
const path = require('path');
const addonName = 'Unicode11Addon';
const mainFile = 'xterm-addon-unicode11.js';
module.exports = {
entry: `./out/${addonName}.js`,
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre",
exclude: /node_modules/
}
]
},
resolve: {
modules: ['./node_modules'],
extensions: [ '.js' ],
alias: {
common: path.resolve('../../out/common')
}
},
output: {
filename: mainFile,
path: path.resolve('./lib'),
library: addonName,
libraryTarget: 'umd'
},
mode: 'production'
};
-2
View File
@@ -1,2 +0,0 @@
lib
node_modules
-5
View File
@@ -1,5 +0,0 @@
**/*.api.js
**/*.api.ts
tsconfig.json
.yarnrc
webpack.config.js
-19
View File
@@ -1,19 +0,0 @@
Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-21
View File
@@ -1,21 +0,0 @@
## xterm-addon-web-links
An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables web links. This addon requires xterm.js v4+.
### Install
```bash
npm install --save xterm-addon-web-links
```
### Usage
```ts
import { Terminal } from 'xterm';
import { WebLinksAddon } from 'xterm-addon-web-links';
const terminal = new Terminal();
terminal.loadAddon(new WebLinksAddon());
```
See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/xterm-addon-web-links/typings/xterm-addon-web-links.d.ts) for more advanced usage.
-21
View File
@@ -1,21 +0,0 @@
{
"name": "xterm-addon-web-links",
"version": "0.4.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
},
"main": "lib/xterm-addon-web-links.js",
"types": "typings/xterm-addon-web-links.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"scripts": {
"build": "../../node_modules/.bin/tsc -p src",
"prepackage": "npm run build",
"package": "../../node_modules/.bin/webpack",
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^4.0.0"
}
}
@@ -1,117 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ILinkProvider, IBufferCellPosition, ILink, Terminal } from 'xterm';
export class WebLinkProvider implements ILinkProvider {
constructor(
private readonly _terminal: Terminal,
private readonly _regex: RegExp,
private readonly _handler: (event: MouseEvent, uri: string) => void
) {
}
public provideLinks(y: number, callback: (links: ILink[] | undefined) => void): void {
callback(LinkComputer.computeLink(y, this._regex, this._terminal, this._handler));
}
}
export class LinkComputer {
public static computeLink(y: number, regex: RegExp, terminal: Terminal, handler: (event: MouseEvent, uri: string) => void): ILink[] {
const rex = new RegExp(regex.source, (regex.flags || '') + 'g');
const [line, startLineIndex] = LinkComputer._translateBufferLineToStringWithWrap(y - 1, false, terminal);
let match;
let stringIndex = -1;
const result: ILink[] = [];
while ((match = rex.exec(line)) !== null) {
const text = match[1];
if (!text) {
// something matched but does not comply with the given matchIndex
// since this is most likely a bug the regex itself we simply do nothing here
console.log('match found without corresponding matchIndex');
break;
}
// Get index, match.index is for the outer match which includes negated chars
// therefore we cannot use match.index directly, instead we search the position
// of the match group in text again
// also correct regex and string search offsets for the next loop run
stringIndex = line.indexOf(text, stringIndex + 1);
rex.lastIndex = stringIndex + text.length;
if (stringIndex < 0) {
// invalid stringIndex (should not have happened)
break;
}
let endX = stringIndex + text.length;
let endY = startLineIndex + 1;
while (endX > terminal.cols) {
endX -= terminal.cols;
endY++;
}
const range = {
start: {
x: stringIndex + 1,
y: startLineIndex + 1
},
end: {
x: endX,
y: endY
}
};
result.push({ range, text, activate: handler });
}
return result;
}
/**
* Gets the entire line for the buffer line
* @param line The line being translated.
* @param trimRight Whether to trim whitespace to the right.
* @param terminal The terminal
*/
private static _translateBufferLineToStringWithWrap(lineIndex: number, trimRight: boolean, terminal: Terminal): [string, number] {
let lineString = '';
let lineWrapsToNext: boolean;
let prevLinesToWrap: boolean;
do {
const line = terminal.buffer.active.getLine(lineIndex);
if (!line) {
break;
}
if (line.isWrapped) {
lineIndex--;
}
prevLinesToWrap = line.isWrapped;
} while (prevLinesToWrap);
const startLineIndex = lineIndex;
do {
const nextLine = terminal.buffer.active.getLine(lineIndex + 1);
lineWrapsToNext = nextLine ? nextLine.isWrapped : false;
const line = terminal.buffer.active.getLine(lineIndex);
if (!line) {
break;
}
lineString += line.translateToString(!lineWrapsToNext && trimRight).substring(0, terminal.cols);
lineIndex++;
} while (lineWrapsToNext);
return [lineString, startLineIndex];
}
}
@@ -1,69 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { openTerminal, pollFor, writeSync, getBrowserType } from '../../../out-test/api/TestUtils';
import { Browser, Page } from 'playwright-core';
const APP = 'http://127.0.0.1:3000/test';
let browser: Browser;
let page: Page;
const width = 800;
const height = 600;
describe('WebLinksAddon', () => {
before(async function(): Promise<any> {
const browserType = getBrowserType();
browser = await browserType.launch({ dumpio: true,
headless: process.argv.indexOf('--headless') !== -1
});
page = await (await browser.newContext()).newPage();
await page.setViewportSize({ width, height });
});
after(async () => await browser.close());
beforeEach(async () => await page.goto(APP));
it('.com', async function(): Promise<any> {
await testHostName('foo.com');
});
it('.com.au', async function(): Promise<any> {
await testHostName('foo.com.au');
});
it('.io', async function(): Promise<any> {
await testHostName('foo.io');
});
});
async function testHostName(hostname: string): Promise<void> {
await openTerminal(page, { rendererType: 'dom', cols: 40 });
await page.evaluate(`window.term.loadAddon(new window.WebLinksAddon())`);
const data = ` http://${hostname} \\r\\n` +
` http://${hostname}/a~b#c~d?e~f \\r\\n` +
` http://${hostname}/colon:test \\r\\n` +
` http://${hostname}/colon:test: \\r\\n` +
`"http://${hostname}/"\\r\\n` +
`\\'http://${hostname}/\\'\\r\\n` +
`http://${hostname}/subpath/+/id`;
await writeSync(page, data);
await pollForLinkAtCell(3, 1, `http://${hostname}`);
await pollForLinkAtCell(3, 2, `http://${hostname}/a~b#c~d?e~f`);
await pollForLinkAtCell(3, 3, `http://${hostname}/colon:test`);
await pollForLinkAtCell(3, 4, `http://${hostname}/colon:test`);
await pollForLinkAtCell(2, 5, `http://${hostname}/`);
await pollForLinkAtCell(2, 6, `http://${hostname}/`);
await pollForLinkAtCell(1, 7, `http://${hostname}/subpath/+/id`);
}
async function pollForLinkAtCell(col: number, row: number, value: string): Promise<void> {
const rowSelector = `.xterm-rows > :nth-child(${row})`;
// Ensure the hover element exists before trying to hover it
await pollFor(page, `!!document.querySelector('${rowSelector} > :nth-child(${col})')`, true);
await pollFor(page, `document.querySelectorAll('${rowSelector} > span[style]').length >= ${value.length}`, true, async () => page.hover(`${rowSelector} > :nth-child(${col})`));
assert.equal(await page.evaluate(`Array.prototype.reduce.call(document.querySelectorAll('${rowSelector} > span[style]'), (a, b) => a + b.textContent, '');`), value);
}
@@ -1,70 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal, ILinkMatcherOptions, ITerminalAddon, IDisposable } from 'xterm';
import { WebLinkProvider } from './WebLinkProvider';
const protocolClause = '(https?:\\/\\/)';
const domainCharacterSet = '[\\da-z\\.-]+';
const negatedDomainCharacterSet = '[^\\da-z\\.-]+';
const domainBodyClause = '(' + domainCharacterSet + ')';
const tldClause = '([a-z\\.]{2,6})';
const ipClause = '((\\d{1,3}\\.){3}\\d{1,3})';
const localHostClause = '(localhost)';
const portClause = '(:\\d{1,5})';
const hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?';
const pathCharacterSet = '(\\/[\\/\\w\\.\\-%~:+@]*)*([^:"\'\\s])';
const pathClause = '(' + pathCharacterSet + ')?';
const queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*';
const queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?';
const hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?';
const negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+';
const bodyClause = hostClause + pathClause + queryStringClause + hashFragmentClause;
const start = '(?:^|' + negatedDomainCharacterSet + ')(';
const end = ')($|' + negatedPathCharacterSet + ')';
const strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end);
function handleLink(event: MouseEvent, uri: string): void {
const newWindow = window.open();
if (newWindow) {
newWindow.opener = null;
newWindow.location.href = uri;
} else {
console.warn('Opening link blocked as opener could not be cleared');
}
}
export class WebLinksAddon implements ITerminalAddon {
private _linkMatcherId: number | undefined;
private _terminal: Terminal | undefined;
private _linkProvider: IDisposable | undefined;
constructor(
private _handler: (event: MouseEvent, uri: string) => void = handleLink,
private _options: ILinkMatcherOptions = {},
private _useLinkProvider: boolean = false
) {
this._options.matchIndex = 1;
}
public activate(terminal: Terminal): void {
this._terminal = terminal;
if (this._useLinkProvider && 'registerLinkProvider' in this._terminal) {
this._linkProvider = this._terminal.registerLinkProvider(new WebLinkProvider(this._terminal, strictUrlRegex, this._handler));
} else {
// TODO: This should be removed eventually
this._linkMatcherId = (this._terminal as Terminal).registerLinkMatcher(strictUrlRegex, this._handler, this._options);
}
}
public dispose(): void {
if (this._linkMatcherId !== undefined && this._terminal !== undefined) {
this._terminal.deregisterLinkMatcher(this._linkMatcherId);
}
this._linkProvider?.dispose();
}
}
@@ -1,36 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal, ILinkMatcherOptions, ITerminalAddon } from 'xterm';
declare module 'xterm-addon-web-links' {
/**
* An xterm.js addon that enables web links.
*/
export class WebLinksAddon implements ITerminalAddon {
/**
* Creates a new web links addon.
* @param handler The callback when the link is called.
* @param options Options for the link matcher.
* @param useLinkProvider Whether to use the new link provider API to create
* the links. This is an option because use of both link matcher (old) and
* link provider (new) may cause issues. Link provider will eventually be
* the default and only option.
*/
constructor(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions, useLinkProvider?: boolean);
/**
* Activates the addon
* @param terminal The terminal the addon is being loaded in.
*/
public activate(terminal: Terminal): void;
/**
* Disposes the addon.
*/
public dispose(): void;
}
}
@@ -1,31 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
const path = require('path');
const addonName = 'WebLinksAddon';
const mainFile = 'xterm-addon-web-links.js';
module.exports = {
entry: `./out/${addonName}.js`,
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre",
exclude: /node_modules/
}
]
},
output: {
filename: mainFile,
path: path.resolve('./lib'),
library: addonName,
libraryTarget: 'umd'
},
mode: 'production'
};
-2
View File
@@ -1,2 +0,0 @@
lib
node_modules
-5
View File
@@ -1,5 +0,0 @@
**/*.api.js
**/*.api.ts
tsconfig.json
.yarnrc
webpack.config.js
-19
View File
@@ -1,19 +0,0 @@
Copyright (c) 2018, The xterm.js authors (https://github.com/xtermjs/xterm.js)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-23
View File
@@ -1,23 +0,0 @@
## xterm-addon-webgl
An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables a WebGL-based renderer. This addon requires xterm.js v4+.
⚠️ This is an experimental addon that is [missing some features and may be unstable](https://github.com/xtermjs/xterm.js/issues?q=is%3Aopen+is%3Aissue+label%3Aarea%2Faddon%2Fwebgl) ⚠️
### Install
```bash
npm install --save xterm-addon-webgl
```
### Usage
```ts
import { Terminal } from 'xterm';
import { WebglAddon } from 'xterm-addon-webgl';
const terminal = new Terminal();
terminal.loadAddon(new WebglAddon());
```
See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/xterm-addon-webgl/typings/xterm-addon-webgl.d.ts) for more advanced usage.
-21
View File
@@ -1,21 +0,0 @@
{
"name": "xterm-addon-webgl",
"version": "0.8.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
},
"main": "lib/xterm-addon-webgl.js",
"types": "typings/xterm-addon-webgl.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"scripts": {
"build": "../../node_modules/.bin/tsc -p src",
"prepackage": "npm run build",
"package": "../../node_modules/.bin/webpack",
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^4.0.0"
}
}
@@ -1,393 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { createProgram, PROJECTION_MATRIX, throwIfFalsy } from './WebglUtils';
import { WebglCharAtlas } from './atlas/WebglCharAtlas';
import { IWebGL2RenderingContext, IWebGLVertexArrayObject, IRenderModel, IRasterizedGlyph } from './Types';
import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_INDICIES_PER_CELL, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_BG_OFFSET } from './RenderModel';
import { fill } from 'common/TypedArrayUtils';
import { slice } from './TypedArray';
import { NULL_CELL_CODE, WHITESPACE_CELL_CODE, Attributes, FgFlags } from 'common/buffer/Constants';
import { Terminal, IBufferLine } from 'xterm';
import { IColorSet, IColor } from 'browser/Types';
import { IRenderDimensions } from 'browser/renderer/Types';
import { AttributeData } from 'common/buffer/AttributeData';
interface IVertices {
attributes: Float32Array;
/**
* These buffers are the ones used to bind to WebGL, the reason there are
* multiple is to allow double buffering to work as you cannot modify the
* buffer while it's being used by the GPU. Having multiple lets us start
* working on the next frame.
*/
attributesBuffers: Float32Array[];
selectionAttributes: Float32Array;
count: number;
}
const enum VertexAttribLocations {
UNIT_QUAD = 0,
CELL_POSITION = 1,
OFFSET = 2,
SIZE = 3,
TEXCOORD = 4,
TEXSIZE = 5
}
const vertexShaderSource = `#version 300 es
layout (location = ${VertexAttribLocations.UNIT_QUAD}) in vec2 a_unitquad;
layout (location = ${VertexAttribLocations.CELL_POSITION}) in vec2 a_cellpos;
layout (location = ${VertexAttribLocations.OFFSET}) in vec2 a_offset;
layout (location = ${VertexAttribLocations.SIZE}) in vec2 a_size;
layout (location = ${VertexAttribLocations.TEXCOORD}) in vec2 a_texcoord;
layout (location = ${VertexAttribLocations.TEXSIZE}) in vec2 a_texsize;
uniform mat4 u_projection;
uniform vec2 u_resolution;
out vec2 v_texcoord;
void main() {
vec2 zeroToOne = (a_offset / u_resolution) + a_cellpos + (a_unitquad * a_size);
gl_Position = u_projection * vec4(zeroToOne, 0.0, 1.0);
v_texcoord = a_texcoord + a_unitquad * a_texsize;
}`;
const fragmentShaderSource = `#version 300 es
precision lowp float;
in vec2 v_texcoord;
uniform sampler2D u_texture;
out vec4 outColor;
void main() {
outColor = texture(u_texture, v_texcoord);
}`;
const INDICES_PER_CELL = 10;
const BYTES_PER_CELL = INDICES_PER_CELL * Float32Array.BYTES_PER_ELEMENT;
const CELL_POSITION_INDICES = 2;
export class GlyphRenderer {
private _atlas: WebglCharAtlas | undefined;
private _program: WebGLProgram;
private _vertexArrayObject: IWebGLVertexArrayObject;
private _projectionLocation: WebGLUniformLocation;
private _resolutionLocation: WebGLUniformLocation;
private _textureLocation: WebGLUniformLocation;
private _atlasTexture: WebGLTexture;
private _attributesBuffer: WebGLBuffer;
private _activeBuffer: number = 0;
private _vertices: IVertices = {
count: 0,
attributes: new Float32Array(0),
attributesBuffers: [
new Float32Array(0),
new Float32Array(0)
],
selectionAttributes: new Float32Array(0)
};
constructor(
private _terminal: Terminal,
private _colors: IColorSet,
private _gl: IWebGL2RenderingContext,
private _dimensions: IRenderDimensions
) {
const gl = this._gl;
const program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource));
this._program = program;
// Uniform locations
this._projectionLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_projection'));
this._resolutionLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_resolution'));
this._textureLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_texture'));
// Create and set the vertex array object
this._vertexArrayObject = gl.createVertexArray();
gl.bindVertexArray(this._vertexArrayObject);
// Setup a_unitquad, this defines the 4 vertices of a rectangle
const unitQuadVertices = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]);
const unitQuadVerticesBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, unitQuadVerticesBuffer);
gl.bufferData(gl.ARRAY_BUFFER, unitQuadVertices, gl.STATIC_DRAW);
gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD);
gl.vertexAttribPointer(VertexAttribLocations.UNIT_QUAD, 2, this._gl.FLOAT, false, 0, 0);
// Setup the unit quad element array buffer, this points to indices in
// unitQuadVertuces to allow is to draw 2 triangles from the vertices
const unitQuadElementIndices = new Uint8Array([0, 1, 3, 0, 2, 3]);
const elementIndicesBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementIndicesBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, unitQuadElementIndices, gl.STATIC_DRAW);
// Setup attributes
this._attributesBuffer = throwIfFalsy(gl.createBuffer());
gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer);
gl.enableVertexAttribArray(VertexAttribLocations.OFFSET);
gl.vertexAttribPointer(VertexAttribLocations.OFFSET, 2, gl.FLOAT, false, BYTES_PER_CELL, 0);
gl.vertexAttribDivisor(VertexAttribLocations.OFFSET, 1);
gl.enableVertexAttribArray(VertexAttribLocations.SIZE);
gl.vertexAttribPointer(VertexAttribLocations.SIZE, 2, gl.FLOAT, false, BYTES_PER_CELL, 2 * Float32Array.BYTES_PER_ELEMENT);
gl.vertexAttribDivisor(VertexAttribLocations.SIZE, 1);
gl.enableVertexAttribArray(VertexAttribLocations.TEXCOORD);
gl.vertexAttribPointer(VertexAttribLocations.TEXCOORD, 2, gl.FLOAT, false, BYTES_PER_CELL, 4 * Float32Array.BYTES_PER_ELEMENT);
gl.vertexAttribDivisor(VertexAttribLocations.TEXCOORD, 1);
gl.enableVertexAttribArray(VertexAttribLocations.TEXSIZE);
gl.vertexAttribPointer(VertexAttribLocations.TEXSIZE, 2, gl.FLOAT, false, BYTES_PER_CELL, 6 * Float32Array.BYTES_PER_ELEMENT);
gl.vertexAttribDivisor(VertexAttribLocations.TEXSIZE, 1);
gl.enableVertexAttribArray(VertexAttribLocations.CELL_POSITION);
gl.vertexAttribPointer(VertexAttribLocations.CELL_POSITION, 2, gl.FLOAT, false, BYTES_PER_CELL, 8 * Float32Array.BYTES_PER_ELEMENT);
gl.vertexAttribDivisor(VertexAttribLocations.CELL_POSITION, 1);
// Setup empty texture atlas
this._atlasTexture = throwIfFalsy(gl.createTexture());
gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 255, 255]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
// Allow drawing of transparent texture
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
// Set viewport
this.onResize();
}
public beginFrame(): boolean {
return this._atlas ? this._atlas.beginFrame() : true;
}
public updateCell(x: number, y: number, code: number, bg: number, fg: number, chars: string): void {
this._updateCell(this._vertices.attributes, x, y, code, bg, fg, chars);
}
private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, bg: number, fg: number, chars?: string): void {
const terminal = this._terminal;
const i = (y * terminal.cols + x) * INDICES_PER_CELL;
// Exit early if this is a null/space character
if (code === NULL_CELL_CODE || code === WHITESPACE_CELL_CODE || code === undefined/* This is used for the right side of wide chars */) {
fill(array, 0, i, i + INDICES_PER_CELL - 1 - CELL_POSITION_INDICES);
return;
}
let rasterizedGlyph: IRasterizedGlyph;
if (!this._atlas) {
return;
}
if (chars && chars.length > 1) {
rasterizedGlyph = this._atlas.getRasterizedGlyphCombinedChar(chars, bg, fg);
} else {
rasterizedGlyph = this._atlas.getRasterizedGlyph(code, bg, fg);
}
// Fill empty if no glyph was found
if (!rasterizedGlyph) {
fill(array, 0, i, i + INDICES_PER_CELL - 1 - CELL_POSITION_INDICES);
return;
}
// a_origin
array[i ] = -rasterizedGlyph.offset.x + this._dimensions.scaledCharLeft;
array[i + 1] = -rasterizedGlyph.offset.y + this._dimensions.scaledCharTop;
// a_size
array[i + 2] = rasterizedGlyph.size.x / this._dimensions.scaledCanvasWidth;
array[i + 3] = rasterizedGlyph.size.y / this._dimensions.scaledCanvasHeight;
// a_texcoord
array[i + 4] = rasterizedGlyph.texturePositionClipSpace.x;
array[i + 5] = rasterizedGlyph.texturePositionClipSpace.y;
// a_texsize
array[i + 6] = rasterizedGlyph.sizeClipSpace.x;
array[i + 7] = rasterizedGlyph.sizeClipSpace.y;
// a_cellpos only changes on resize
}
public updateSelection(model: IRenderModel): void {
const terminal = this._terminal;
this._vertices.selectionAttributes = slice(this._vertices.attributes, 0);
const bg = (this._colors.selectionOpaque.rgba >>> 8) | Attributes.CM_RGB;
if (model.selection.columnSelectMode) {
const startCol = model.selection.startCol;
const width = model.selection.endCol - startCol;
const height = model.selection.viewportCappedEndRow - model.selection.viewportCappedStartRow + 1;
for (let y = model.selection.viewportCappedStartRow; y < model.selection.viewportCappedStartRow + height; y++) {
this._updateSelectionRange(startCol, startCol + width, y, model, bg);
}
} else {
// Draw first row
const startCol = model.selection.viewportStartRow === model.selection.viewportCappedStartRow ? model.selection.startCol : 0;
const startRowEndCol = model.selection.viewportCappedStartRow === model.selection.viewportCappedEndRow ? model.selection.endCol : terminal.cols;
this._updateSelectionRange(startCol, startRowEndCol, model.selection.viewportCappedStartRow, model, bg);
// Draw middle rows
const middleRowsCount = Math.max(model.selection.viewportCappedEndRow - model.selection.viewportCappedStartRow - 1, 0);
for (let y = model.selection.viewportCappedStartRow + 1; y <= model.selection.viewportCappedStartRow + middleRowsCount; y++) {
this._updateSelectionRange(0, startRowEndCol, y, model, bg);
}
// Draw final row
if (model.selection.viewportCappedStartRow !== model.selection.viewportCappedEndRow) {
// Only draw viewportEndRow if it's not the same as viewportStartRow
const endCol = model.selection.viewportEndRow === model.selection.viewportCappedEndRow ? model.selection.endCol : terminal.cols;
this._updateSelectionRange(0, endCol, model.selection.viewportCappedEndRow, model, bg);
}
}
}
private _updateSelectionRange(startCol: number, endCol: number, y: number, model: IRenderModel, bg: number): void {
const terminal = this._terminal;
const row = y + terminal.buffer.active.viewportY;
let line: IBufferLine | undefined;
for (let x = startCol; x < endCol; x++) {
const offset = (y * this._terminal.cols + x) * RENDER_MODEL_INDICIES_PER_CELL;
const code = model.cells[offset];
let fg = model.cells[offset + RENDER_MODEL_FG_OFFSET];
if (fg & FgFlags.INVERSE) {
const workCell = new AttributeData();
workCell.fg = fg;
workCell.bg = model.cells[offset + RENDER_MODEL_BG_OFFSET];
// 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
// color, not on the selection color
fg = (fg & ~(Attributes.CM_MASK | Attributes.RGB_MASK | FgFlags.INVERSE));
switch (workCell.getBgColorMode()) {
case Attributes.CM_P16:
case Attributes.CM_P256:
const c = this._getColorFromAnsiIndex(workCell.getBgColor()).rgba;
fg |= (c >> 8) & Attributes.RED_MASK | (c >> 8) & Attributes.GREEN_MASK | (c >> 8) & Attributes.BLUE_MASK;
case Attributes.CM_RGB:
const arr = AttributeData.toColorRGB(workCell.getBgColor());
fg |= arr[0] << Attributes.RED_SHIFT | arr[1] << Attributes.GREEN_SHIFT | arr[2] << Attributes.BLUE_SHIFT;
case Attributes.CM_DEFAULT:
default:
const c2 = this._colors.background.rgba;
fg |= (c2 >> 8) & Attributes.RED_MASK | (c2 >> 8) & Attributes.GREEN_MASK | (c2 >> 8) & Attributes.BLUE_MASK;
}
fg |= Attributes.CM_RGB;
}
if (code & COMBINED_CHAR_BIT_MASK) {
if (!line) {
line = terminal.buffer.active.getLine(row);
}
const chars = line!.getCell(x)!.getChars();
this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], bg, fg, chars);
} else {
this._updateCell(this._vertices.selectionAttributes, x, y, model.cells[offset], bg, fg);
}
}
}
private _getColorFromAnsiIndex(idx: number): IColor {
if (idx >= this._colors.ansi.length) {
throw new Error('No color found for idx ' + idx);
}
return this._colors.ansi[idx];
}
public onResize(): void {
const terminal = this._terminal;
const gl = this._gl;
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Update vertices
const newCount = terminal.cols * terminal.rows * INDICES_PER_CELL;
if (this._vertices.count !== newCount) {
this._vertices.count = newCount;
this._vertices.attributes = new Float32Array(newCount);
for (let i = 0; i < this._vertices.attributesBuffers.length; i++) {
this._vertices.attributesBuffers[i] = new Float32Array(newCount);
}
let i = 0;
for (let y = 0; y < terminal.rows; y++) {
for (let x = 0; x < terminal.cols; x++) {
this._vertices.attributes[i + 8] = x / terminal.cols;
this._vertices.attributes[i + 9] = y / terminal.rows;
i += INDICES_PER_CELL;
}
}
}
}
public setColors(): void {
}
public render(renderModel: IRenderModel, isSelectionVisible: boolean): void {
if (!this._atlas) {
return;
}
const gl = this._gl;
gl.useProgram(this._program);
gl.bindVertexArray(this._vertexArrayObject);
// Alternate buffers each frame as the active buffer gets locked while it's in use by the GPU
this._activeBuffer = (this._activeBuffer + 1) % 2;
const activeBuffer = this._vertices.attributesBuffers[this._activeBuffer];
// Copy data for each cell of each line up to its line length (the last non-whitespace cell)
// from the attributes buffer into activeBuffer, which is the one that gets bound to the GPU.
// The reasons for this are as follows:
// - So the active buffer can be alternated so we don't get blocked on rendering finishing
// - To copy either the normal attributes buffer or the selection attributes buffer when there
// is a selection
// - So we don't send vertices for all the line-ending whitespace to the GPU
let bufferLength = 0;
for (let y = 0; y < renderModel.lineLengths.length; y++) {
const si = y * this._terminal.cols * INDICES_PER_CELL;
const sub = (isSelectionVisible ? this._vertices.selectionAttributes : this._vertices.attributes).subarray(si, si + renderModel.lineLengths[y] * INDICES_PER_CELL);
activeBuffer.set(sub, bufferLength);
bufferLength += sub.length;
}
// Bind the attributes buffer
gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer);
gl.bufferData(gl.ARRAY_BUFFER, activeBuffer.subarray(0, bufferLength), gl.STREAM_DRAW);
// Bind the texture atlas if it's changed
if (this._atlas.hasCanvasChanged) {
this._atlas.hasCanvasChanged = false;
gl.uniform1i(this._textureLocation, 0);
gl.activeTexture(gl.TEXTURE0 + 0);
gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._atlas.cacheCanvas);
gl.generateMipmap(gl.TEXTURE_2D);
}
// Set uniforms
gl.uniformMatrix4fv(this._projectionLocation, false, PROJECTION_MATRIX);
gl.uniform2f(this._resolutionLocation, gl.canvas.width, gl.canvas.height);
// Draw the viewport
gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, bufferLength / INDICES_PER_CELL);
}
public setAtlas(atlas: WebglCharAtlas): void {
const gl = this._gl;
this._atlas = atlas;
gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, atlas.cacheCanvas);
gl.generateMipmap(gl.TEXTURE_2D);
}
public setDimensions(dimensions: IRenderDimensions): void {
this._dimensions = dimensions;
}
}
@@ -1,350 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { createProgram, expandFloat32Array, PROJECTION_MATRIX, throwIfFalsy } from './WebglUtils';
import { IRenderModel, IWebGLVertexArrayObject, IWebGL2RenderingContext, ISelectionRenderModel } from './Types';
import { fill } from 'common/TypedArrayUtils';
import { Attributes, FgFlags } from 'common/buffer/Constants';
import { Terminal } from 'xterm';
import { IColorSet, IColor } from 'browser/Types';
import { IRenderDimensions } from 'browser/renderer/Types';
import { RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel';
const enum VertexAttribLocations {
POSITION = 0,
SIZE = 1,
COLOR = 2,
UNIT_QUAD = 3
}
const vertexShaderSource = `#version 300 es
layout (location = ${VertexAttribLocations.POSITION}) in vec2 a_position;
layout (location = ${VertexAttribLocations.SIZE}) in vec2 a_size;
layout (location = ${VertexAttribLocations.COLOR}) in vec4 a_color;
layout (location = ${VertexAttribLocations.UNIT_QUAD}) in vec2 a_unitquad;
uniform mat4 u_projection;
uniform vec2 u_resolution;
out vec4 v_color;
void main() {
vec2 zeroToOne = (a_position + (a_unitquad * a_size)) / u_resolution;
gl_Position = u_projection * vec4(zeroToOne, 0.0, 1.0);
v_color = a_color;
}`;
const fragmentShaderSource = `#version 300 es
precision lowp float;
in vec4 v_color;
out vec4 outColor;
void main() {
outColor = v_color;
}`;
interface IVertices {
attributes: Float32Array;
selection: Float32Array;
count: number;
}
const INDICES_PER_RECTANGLE = 8;
const BYTES_PER_RECTANGLE = INDICES_PER_RECTANGLE * Float32Array.BYTES_PER_ELEMENT;
const INITIAL_BUFFER_RECTANGLE_CAPACITY = 20 * INDICES_PER_RECTANGLE;
export class RectangleRenderer {
private _program: WebGLProgram;
private _vertexArrayObject: IWebGLVertexArrayObject;
private _resolutionLocation: WebGLUniformLocation;
private _attributesBuffer: WebGLBuffer;
private _projectionLocation: WebGLUniformLocation;
private _bgFloat!: Float32Array;
private _selectionFloat!: Float32Array;
private _vertices: IVertices = {
count: 0,
attributes: new Float32Array(INITIAL_BUFFER_RECTANGLE_CAPACITY),
selection: new Float32Array(3 * INDICES_PER_RECTANGLE)
};
constructor(
private _terminal: Terminal,
private _colors: IColorSet,
private _gl: IWebGL2RenderingContext,
private _dimensions: IRenderDimensions
) {
const gl = this._gl;
this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource));
// Uniform locations
this._resolutionLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_resolution'));
this._projectionLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_projection'));
// Create and set the vertex array object
this._vertexArrayObject = gl.createVertexArray();
gl.bindVertexArray(this._vertexArrayObject);
// Setup a_unitquad, this defines the 4 vertices of a rectangle
const unitQuadVertices = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]);
const unitQuadVerticesBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, unitQuadVerticesBuffer);
gl.bufferData(gl.ARRAY_BUFFER, unitQuadVertices, gl.STATIC_DRAW);
gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD);
gl.vertexAttribPointer(VertexAttribLocations.UNIT_QUAD, 2, this._gl.FLOAT, false, 0, 0);
// Setup the unit quad element array buffer, this points to indices in
// unitQuadVertuces to allow is to draw 2 triangles from the vertices
const unitQuadElementIndices = new Uint8Array([0, 1, 3, 0, 2, 3]);
const elementIndicesBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementIndicesBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, unitQuadElementIndices, gl.STATIC_DRAW);
// Setup attributes
this._attributesBuffer = throwIfFalsy(gl.createBuffer());
gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer);
gl.enableVertexAttribArray(VertexAttribLocations.POSITION);
gl.vertexAttribPointer(VertexAttribLocations.POSITION, 2, gl.FLOAT, false, BYTES_PER_RECTANGLE, 0);
gl.vertexAttribDivisor(VertexAttribLocations.POSITION, 1);
gl.enableVertexAttribArray(VertexAttribLocations.SIZE);
gl.vertexAttribPointer(VertexAttribLocations.SIZE, 2, gl.FLOAT, false, BYTES_PER_RECTANGLE, 2 * Float32Array.BYTES_PER_ELEMENT);
gl.vertexAttribDivisor(VertexAttribLocations.SIZE, 1);
gl.enableVertexAttribArray(VertexAttribLocations.COLOR);
gl.vertexAttribPointer(VertexAttribLocations.COLOR, 4, gl.FLOAT, false, BYTES_PER_RECTANGLE, 4 * Float32Array.BYTES_PER_ELEMENT);
gl.vertexAttribDivisor(VertexAttribLocations.COLOR, 1);
this._updateCachedColors();
}
public render(): void {
const gl = this._gl;
gl.useProgram(this._program);
gl.bindVertexArray(this._vertexArrayObject);
gl.uniformMatrix4fv(this._projectionLocation, false, PROJECTION_MATRIX);
gl.uniform2f(this._resolutionLocation, gl.canvas.width, gl.canvas.height);
// Bind attributes buffer and draw
gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this._vertices.attributes, gl.DYNAMIC_DRAW);
gl.drawElementsInstanced(this._gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, this._vertices.count);
// Bind selection buffer and draw
gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer);
gl.bufferData(gl.ARRAY_BUFFER, this._vertices.selection, gl.DYNAMIC_DRAW);
gl.drawElementsInstanced(this._gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 3);
}
public onResize(): void {
this._updateViewportRectangle();
}
public setColors(): void {
this._updateCachedColors();
this._updateViewportRectangle();
}
private _updateCachedColors(): void {
this._bgFloat = this._colorToFloat32Array(this._colors.background);
this._selectionFloat = this._colorToFloat32Array(this._colors.selectionOpaque);
}
private _updateViewportRectangle(): void {
// Set first rectangle that clears the screen
this._addRectangleFloat(
this._vertices.attributes,
0,
0,
0,
this._terminal.cols * this._dimensions.scaledCellWidth,
this._terminal.rows * this._dimensions.scaledCellHeight,
this._bgFloat
);
}
public updateSelection(model: ISelectionRenderModel): void {
const terminal = this._terminal;
if (!model.hasSelection) {
fill(this._vertices.selection, 0, 0);
return;
}
if (model.columnSelectMode) {
const startCol = model.startCol;
const width = model.endCol - startCol;
const height = model.viewportCappedEndRow - model.viewportCappedStartRow + 1;
this._addRectangleFloat(
this._vertices.selection,
0,
startCol * this._dimensions.scaledCellWidth,
model.viewportCappedStartRow * this._dimensions.scaledCellHeight,
width * this._dimensions.scaledCellWidth,
height * this._dimensions.scaledCellHeight,
this._selectionFloat
);
fill(this._vertices.selection, 0, INDICES_PER_RECTANGLE);
} else {
// Draw first row
const startCol = model.viewportStartRow === model.viewportCappedStartRow ? model.startCol : 0;
const startRowEndCol = model.viewportCappedStartRow === model.viewportCappedEndRow ? model.endCol : terminal.cols;
this._addRectangleFloat(
this._vertices.selection,
0,
startCol * this._dimensions.scaledCellWidth,
model.viewportCappedStartRow * this._dimensions.scaledCellHeight,
(startRowEndCol - startCol) * this._dimensions.scaledCellWidth,
this._dimensions.scaledCellHeight,
this._selectionFloat
);
// Draw middle rows
const middleRowsCount = Math.max(model.viewportCappedEndRow - model.viewportCappedStartRow - 1, 0);
this._addRectangleFloat(
this._vertices.selection,
INDICES_PER_RECTANGLE,
0,
(model.viewportCappedStartRow + 1) * this._dimensions.scaledCellHeight,
terminal.cols * this._dimensions.scaledCellWidth,
middleRowsCount * this._dimensions.scaledCellHeight,
this._selectionFloat
);
// Draw final row
if (model.viewportCappedStartRow !== model.viewportCappedEndRow) {
// Only draw viewportEndRow if it's not the same as viewportStartRow
const endCol = model.viewportEndRow === model.viewportCappedEndRow ? model.endCol : terminal.cols;
this._addRectangleFloat(
this._vertices.selection,
INDICES_PER_RECTANGLE * 2,
0,
model.viewportCappedEndRow * this._dimensions.scaledCellHeight,
endCol * this._dimensions.scaledCellWidth,
this._dimensions.scaledCellHeight,
this._selectionFloat
);
} else {
fill(this._vertices.selection, 0, INDICES_PER_RECTANGLE * 2);
}
}
}
public updateBackgrounds(model: IRenderModel): void {
const terminal = this._terminal;
const vertices = this._vertices;
let rectangleCount = 1;
for (let y = 0; y < terminal.rows; y++) {
let currentStartX = -1;
let currentBg = 0;
let currentFg = 0;
let currentInverse = false;
for (let x = 0; x < terminal.cols; x++) {
const modelIndex = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
const bg = model.cells[modelIndex + RENDER_MODEL_BG_OFFSET];
const fg = model.cells[modelIndex + RENDER_MODEL_FG_OFFSET];
const inverse = !!(fg & FgFlags.INVERSE);
if (bg !== currentBg || (fg !== currentFg && (currentInverse || inverse))) {
// A rectangle needs to be drawn if going from non-default to another color
if (currentBg !== 0 || (currentInverse && currentFg !== 0)) {
const offset = rectangleCount++ * INDICES_PER_RECTANGLE;
this._updateRectangle(vertices, offset, currentFg, currentBg, currentStartX, x, y);
}
currentStartX = x;
currentBg = bg;
currentFg = fg;
currentInverse = inverse;
}
}
// Finish rectangle if it's still going
if (currentBg !== 0 || (currentInverse && currentFg !== 0)) {
const offset = rectangleCount++ * INDICES_PER_RECTANGLE;
this._updateRectangle(vertices, offset, currentFg, currentBg, currentStartX, terminal.cols, y);
}
}
vertices.count = rectangleCount;
}
private _updateRectangle(vertices: IVertices, offset: number, fg: number, bg: number, startX: number, endX: number, y: number): void {
let rgba: number | undefined;
if (fg & FgFlags.INVERSE) {
switch (fg & Attributes.CM_MASK) {
case Attributes.CM_P16:
case Attributes.CM_P256:
rgba = this._colors.ansi[fg & Attributes.PCOLOR_MASK].rgba;
break;
case Attributes.CM_RGB:
rgba = (fg & Attributes.RGB_MASK) << 8;
break;
case Attributes.CM_DEFAULT:
default:
rgba = this._colors.foreground.rgba;
}
} else {
switch (bg & Attributes.CM_MASK) {
case Attributes.CM_P16:
case Attributes.CM_P256:
rgba = this._colors.ansi[bg & Attributes.PCOLOR_MASK].rgba;
break;
case Attributes.CM_RGB:
rgba = (bg & Attributes.RGB_MASK) << 8;
break;
case Attributes.CM_DEFAULT:
default:
rgba = this._colors.background.rgba;
}
}
if (vertices.attributes.length < offset + 4) {
vertices.attributes = expandFloat32Array(vertices.attributes, this._terminal.rows * this._terminal.cols * INDICES_PER_RECTANGLE);
}
const x1 = startX * this._dimensions.scaledCellWidth;
const y1 = y * this._dimensions.scaledCellHeight;
const r = ((rgba >> 24) & 0xFF) / 255;
const g = ((rgba >> 16) & 0xFF) / 255;
const b = ((rgba >> 8 ) & 0xFF) / 255;
this._addRectangle(vertices.attributes, offset, x1, y1, (endX - startX) * this._dimensions.scaledCellWidth, this._dimensions.scaledCellHeight, r, g, b, 1);
}
private _addRectangle(array: Float32Array, offset: number, x1: number, y1: number, width: number, height: number, r: number, g: number, b: number, a: number): void {
array[offset ] = x1;
array[offset + 1] = y1;
array[offset + 2] = width;
array[offset + 3] = height;
array[offset + 4] = r;
array[offset + 5] = g;
array[offset + 6] = b;
array[offset + 7] = a;
}
private _addRectangleFloat(array: Float32Array, offset: number, x1: number, y1: number, width: number, height: number, color: Float32Array): void {
array[offset ] = x1;
array[offset + 1] = y1;
array[offset + 2] = width;
array[offset + 3] = height;
array[offset + 4] = color[0];
array[offset + 5] = color[1];
array[offset + 6] = color[2];
array[offset + 7] = color[3];
}
private _colorToFloat32Array(color: IColor): Float32Array {
return new Float32Array([
((color.rgba >> 24) & 0xFF) / 255,
((color.rgba >> 16) & 0xFF) / 255,
((color.rgba >> 8 ) & 0xFF) / 255,
((color.rgba ) & 0xFF) / 255
]);
}
}
@@ -1,58 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IRenderModel, ISelectionRenderModel } from './Types';
import { fill } from 'common/TypedArrayUtils';
export const RENDER_MODEL_INDICIES_PER_CELL = 3;
export const RENDER_MODEL_BG_OFFSET = 1;
export const RENDER_MODEL_FG_OFFSET = 2;
export const COMBINED_CHAR_BIT_MASK = 0x80000000;
export class RenderModel implements IRenderModel {
public cells: Uint32Array;
public lineLengths: Uint32Array;
public selection: ISelectionRenderModel;
constructor() {
this.cells = new Uint32Array(0);
this.lineLengths = new Uint32Array(0);
this.selection = {
hasSelection: false,
columnSelectMode: false,
viewportStartRow: 0,
viewportEndRow: 0,
viewportCappedStartRow: 0,
viewportCappedEndRow: 0,
startCol: 0,
endCol: 0
};
}
public resize(cols: number, rows: number): void {
const indexCount = cols * rows * RENDER_MODEL_INDICIES_PER_CELL;
if (indexCount !== this.cells.length) {
this.cells = new Uint32Array(indexCount);
this.lineLengths = new Uint32Array(rows);
}
}
public clear(): void {
fill(this.cells, 0, 0);
fill(this.lineLengths, 0, 0);
this.clearSelection();
}
public clearSelection(): void {
this.selection.hasSelection = false;
this.selection.viewportStartRow = 0;
this.selection.viewportEndRow = 0;
this.selection.viewportCappedStartRow = 0;
this.selection.viewportCappedEndRow = 0;
this.selection.startCol = 0;
this.selection.endCol = 0;
}
}
@@ -1,120 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { sliceFallback } from './TypedArray';
type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | Int8Array | Int16Array | Int32Array | Float32Array | Float64Array;
function deepEquals(a: TypedArray, b: TypedArray): void {
assert.equal(a.length, b.length);
for (let i = 0; i < a.length; ++i) {
assert.equal(a[i], b[i]);
}
}
describe('polyfill conformance tests', function(): void {
describe('TypedArray.slice', () => {
describe('should work with all typed array types', () => {
it('Uint8Array', () => {
const a = new Uint8Array(5);
deepEquals(sliceFallback(a, 2), a.slice(2));
deepEquals(sliceFallback(a, 65535), a.slice(65535));
deepEquals(sliceFallback(a, -1), a.slice(-1));
});
it('Uint16Array', () => {
const u161 = new Uint16Array(5);
const u162 = new Uint16Array(5);
deepEquals(sliceFallback(u161, 2), u162.slice(2));
deepEquals(sliceFallback(u161, 65535), u162.slice(65535));
deepEquals(sliceFallback(u161, -1), u162.slice(-1));
});
it('Uint32Array', () => {
const u321 = new Uint32Array(5);
const u322 = new Uint32Array(5);
deepEquals(sliceFallback(u321, 2), u322.slice(2));
deepEquals(sliceFallback(u321, 65537), u322.slice(65537));
deepEquals(sliceFallback(u321, -1), u322.slice(-1));
});
it('Int8Array', () => {
const i81 = new Int8Array(5);
const i82 = new Int8Array(5);
deepEquals(sliceFallback(i81, 2), i82.slice(2));
deepEquals(sliceFallback(i81, 65537), i82.slice(65537));
deepEquals(sliceFallback(i81, -1), i82.slice(-1));
});
it('Int16Array', () => {
const i161 = new Int16Array(5);
const i162 = new Int16Array(5);
deepEquals(sliceFallback(i161, 2), i162.slice(2));
deepEquals(sliceFallback(i161, 65535), i162.slice(65535));
deepEquals(sliceFallback(i161, -1), i162.slice(-1));
});
it('Int32Array', () => {
const i321 = new Int32Array(5);
const i322 = new Int32Array(5);
deepEquals(sliceFallback(i321, 2), i322.slice(2));
deepEquals(sliceFallback(i321, 65537), i322.slice(65537));
deepEquals(sliceFallback(i321, -1), i322.slice(-1));
});
it('Float32Array', () => {
const f321 = new Float32Array(5);
const f322 = new Float32Array(5);
deepEquals(sliceFallback(f321, 2), f322.slice(2));
deepEquals(sliceFallback(f321, 65537), f322.slice(65537));
deepEquals(sliceFallback(f321, -1), f322.slice(-1));
});
it('Float64Array', () => {
const f641 = new Float64Array(5);
const f642 = new Float64Array(5);
deepEquals(sliceFallback(f641, 2), f642.slice(2));
deepEquals(sliceFallback(f641, 65537), f642.slice(65537));
deepEquals(sliceFallback(f641, -1), f642.slice(-1));
});
it('Uint8ClampedArray', () => {
const u8Clamped1 = new Uint8ClampedArray(5);
const u8Clamped2 = new Uint8ClampedArray(5);
deepEquals(sliceFallback(u8Clamped1, 2), u8Clamped2.slice(2));
deepEquals(sliceFallback(u8Clamped1, 65537), u8Clamped2.slice(65537));
deepEquals(sliceFallback(u8Clamped1, -1), u8Clamped2.slice(-1));
});
});
it('start', () => {
const arr = new Uint32Array([1, 2, 3, 4, 5]);
deepEquals(sliceFallback(arr, -1), arr.slice(-1));
deepEquals(sliceFallback(arr, 0), arr.slice(0));
deepEquals(sliceFallback(arr, 1), arr.slice(1));
deepEquals(sliceFallback(arr, 2), arr.slice(2));
deepEquals(sliceFallback(arr, 3), arr.slice(3));
deepEquals(sliceFallback(arr, 4), arr.slice(4));
deepEquals(sliceFallback(arr, 5), arr.slice(5));
});
it('end', () => {
const arr = new Uint32Array([1, 2, 3, 4, 5]);
deepEquals(sliceFallback(arr, -1, -2), arr.slice(-1, -2));
deepEquals(sliceFallback(arr, 0, -2), arr.slice(0, -2));
deepEquals(sliceFallback(arr, 1, -2), arr.slice(1, -2));
deepEquals(sliceFallback(arr, 2, -2), arr.slice(2, -2));
deepEquals(sliceFallback(arr, 3, -2), arr.slice(3, -2));
deepEquals(sliceFallback(arr, 4, -2), arr.slice(4, -2));
deepEquals(sliceFallback(arr, 5, -2), arr.slice(5, -2));
deepEquals(sliceFallback(arr, -1, 3), arr.slice(-1, 3));
deepEquals(sliceFallback(arr, 0, 3), arr.slice(0, 3));
deepEquals(sliceFallback(arr, 1, 3), arr.slice(1, 3));
deepEquals(sliceFallback(arr, 2, 3), arr.slice(2, 3));
deepEquals(sliceFallback(arr, 3, 3), arr.slice(3, 3));
deepEquals(sliceFallback(arr, 4, 3), arr.slice(4, 3));
deepEquals(sliceFallback(arr, 5, 3), arr.slice(5, 3));
deepEquals(sliceFallback(arr, -1, 8), arr.slice(-1, 8));
deepEquals(sliceFallback(arr, 0, 8), arr.slice(0, 8));
deepEquals(sliceFallback(arr, 1, 8), arr.slice(1, 8));
deepEquals(sliceFallback(arr, 2, 8), arr.slice(2, 8));
deepEquals(sliceFallback(arr, 3, 8), arr.slice(3, 8));
deepEquals(sliceFallback(arr, 4, 8), arr.slice(4, 8));
deepEquals(sliceFallback(arr, 5, 8), arr.slice(5, 8));
});
});
});
@@ -1,32 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
export type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | Int8Array | Int16Array | Int32Array | Float32Array | Float64Array;
export function slice<T extends TypedArray>(array: T, start?: number, end?: number): T {
// all modern engines that support .slice
if (array.slice) {
return array.slice(start, end) as T;
}
return sliceFallback(array, start, end);
}
export function sliceFallback<T extends TypedArray>(array: T, start: number = 0, end: number = array.length): T {
if (start < 0) {
start = (array.length + start) % array.length;
}
if (end >= array.length) {
end = array.length;
} else {
end = (array.length + end) % array.length;
}
start = Math.min(start, end);
const result: T = new (array.constructor as any)(end - start);
for (let i = 0; i < end - start; ++i) {
result[i] = array[i + start];
}
return result;
}
-76
View File
@@ -1,76 +0,0 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
export interface IRasterizedGlyphSet {
[bg: number]: { [fg: number]: IRasterizedGlyph } | undefined;
}
/**
* Represents a rasterized glyph within a texture atlas. Some numbers are
* tracked in CSS pixels as well in order to reduce calculations during the
* render loop.
*/
export interface IRasterizedGlyph {
/**
* The x and y offset between the glyph's top/left and the top/left of a cell
* in pixels.
*/
offset: IVector;
/**
* the x and y position of the glyph in the texture in pixels.
*/
texturePosition: IVector;
/**
* the x and y position of the glyph in the texture in clip space coordinates.
*/
texturePositionClipSpace: IVector;
/**
* The width and height of the glyph in the texture in pixels.
*/
size: IVector;
/**
* The width and height of the glyph in the texture in clip space coordinates.
*/
sizeClipSpace: IVector;
}
export interface IVector {
x: number;
y: number;
}
export interface IBoundingBox {
top: number;
left: number;
right: number;
bottom: number;
}
export interface IRenderModel {
cells: Uint32Array;
lineLengths: Uint32Array;
selection: ISelectionRenderModel;
}
export interface ISelectionRenderModel {
hasSelection: boolean;
columnSelectMode: boolean;
viewportStartRow: number;
viewportEndRow: number;
viewportCappedStartRow: number;
viewportCappedEndRow: number;
startCol: number;
endCol: number;
}
export interface IWebGL2RenderingContext extends WebGLRenderingContext {
vertexAttribDivisor(index: number, divisor: number): void;
createVertexArray(): IWebGLVertexArrayObject;
bindVertexArray(vao: IWebGLVertexArrayObject): void;
drawElementsInstanced(mode: number, count: number, type: number, offset: number, instanceCount: number): void;
}
export interface IWebGLVertexArrayObject {
}
@@ -1,43 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal, ITerminalAddon } from 'xterm';
import { WebglRenderer } from './WebglRenderer';
import { IRenderService } from 'browser/services/Services';
import { IColorSet } from 'browser/Types';
export class WebglAddon implements ITerminalAddon {
private _terminal?: Terminal;
private _renderer?: WebglRenderer;
constructor(
private _preserveDrawingBuffer?: boolean
) {}
public activate(terminal: Terminal): void {
if (!terminal.element) {
throw new Error('Cannot activate WebglAddon before Terminal.open');
}
this._terminal = terminal;
const renderService: IRenderService = (<any>terminal)._core._renderService;
const colors: IColorSet = (<any>terminal)._core._colorManager.colors;
this._renderer = new WebglRenderer(terminal, colors, this._preserveDrawingBuffer);
renderService.setRenderer(this._renderer);
}
public dispose(): void {
if (!this._terminal) {
throw new Error('Cannot dispose WebglAddon because it is activated');
}
const renderService: IRenderService = (this._terminal as any)._core._renderService;
renderService.setRenderer((this._terminal as any)._core._createRenderer());
renderService.onResize(this._terminal.cols, this._terminal.rows);
this._renderer = undefined;
}
public get textureAtlas(): HTMLCanvasElement | undefined {
return this._renderer?.textureAtlas;
}
}

Some files were not shown because too many files have changed in this diff Show More