mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into feature-778
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
language: node_js
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
node_js:
|
||||
- 6
|
||||
before_install:
|
||||
|
||||
@@ -119,6 +119,9 @@ computational environment for Jupyter, supporting interactive data science and s
|
||||
- [**Whack Whack Terminal**](https://github.com/Microsoft/WhackWhackTerminal): Terminal emulator for Visual Studio 2017.
|
||||
- [**VTerm**](https://github.com/vterm/vterm): Extensible terminal emulator based on Electron and React.
|
||||
- [**electerm**](http://electerm.html5beta.com): electerm is a terminal/ssh/sftp client(mac, win, linux) based on electron/node-pty/xterm.
|
||||
- [**Kubebox**](https://github.com/astefanutti/kubebox): Terminal console for Kubernetes clusters.
|
||||
- [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure.
|
||||
- [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
+38
-46
@@ -18,11 +18,13 @@ const ts = require('gulp-typescript');
|
||||
const util = require('gulp-util');
|
||||
const webpack = require('webpack-stream');
|
||||
|
||||
let buildDir = process.env.BUILD_DIR || 'build';
|
||||
let tsProject = ts.createProject('tsconfig.json');
|
||||
let srcDir = tsProject.config.compilerOptions.rootDir;
|
||||
const buildDir = process.env.BUILD_DIR || 'build';
|
||||
const tsProject = ts.createProject('tsconfig.json');
|
||||
const srcDir = tsProject.config.compilerOptions.rootDir;
|
||||
let outDir = tsProject.config.compilerOptions.outDir;
|
||||
|
||||
const addons = ['attach', 'fit', 'fullscreen', 'search', 'terminado', 'winptyCompat', 'zmodem'];
|
||||
|
||||
// Under some environments like TravisCI, this comes out at absolute which can
|
||||
// break the build. This ensures that the outDir is absolute.
|
||||
if (path.normalize(outDir).indexOf(__dirname) !== 0) {
|
||||
@@ -45,15 +47,19 @@ gulp.task('tsc', function () {
|
||||
tsResult.dts.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: ''})).pipe(gulp.dest(outDir))
|
||||
);
|
||||
|
||||
let addons = ['attach', 'fit', 'fullscreen', 'search', 'terminado', 'winptyCompat', 'zmodem'];
|
||||
let addonStreams = addons.map(function(addon) {
|
||||
fs.emptyDirSync(`${outDir}/addons/${addon}`);
|
||||
|
||||
let tsProjectAddon = ts.createProject(`./src/addons/${addon}/tsconfig.json`);
|
||||
let tsResultAddon = tsProjectAddon.src().pipe(sourcemaps.init()).pipe(tsProjectAddon());
|
||||
let tscAddon = tsResultAddon.js
|
||||
.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: ''}))
|
||||
.pipe(gulp.dest(`${outDir}/addons/${addon}`));
|
||||
let tscAddon = merge(
|
||||
tsResultAddon.js
|
||||
.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: ''}))
|
||||
.pipe(gulp.dest(`${outDir}/addons/${addon}`)),
|
||||
tsResultAddon.dts
|
||||
.pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: ''}))
|
||||
.pipe(gulp.dest(`${outDir}/addons/${addon}`))
|
||||
)
|
||||
|
||||
return tscAddon;
|
||||
});
|
||||
@@ -103,45 +109,29 @@ gulp.task('browserify', ['tsc'], function() {
|
||||
});
|
||||
|
||||
gulp.task('browserify-addons', ['tsc'], function() {
|
||||
let searchOptions = {
|
||||
basedir: `${buildDir}/addons/search`,
|
||||
debug: true,
|
||||
entries: [`${outDir}/addons/search/search.js`],
|
||||
cache: {},
|
||||
packageCache: {}
|
||||
};
|
||||
let searchBundle = browserify(searchOptions)
|
||||
.external(path.join(outDir, 'Terminal.js'))
|
||||
.bundle()
|
||||
.pipe(source('./addons/search/search.js'))
|
||||
.pipe(buffer())
|
||||
.pipe(sourcemaps.init({loadMaps: true, sourceRoot: ''}))
|
||||
.pipe(sourcemaps.write('./'))
|
||||
.pipe(gulp.dest(buildDir));
|
||||
const bundles = addons.map((addon) => {
|
||||
const addonOptions = {
|
||||
basedir: `${buildDir}/addons/${addon}`,
|
||||
debug: true,
|
||||
entries: [`${outDir}/addons/${addon}/${addon}.js`],
|
||||
standalone: addon,
|
||||
cache: {},
|
||||
packageCache: {}
|
||||
};
|
||||
|
||||
let winptyCompatOptions = {
|
||||
basedir: `${buildDir}/addons/winptyCompat`,
|
||||
debug: true,
|
||||
entries: [`${outDir}/addons/winptyCompat/winptyCompat.js`],
|
||||
cache: {},
|
||||
packageCache: {}
|
||||
};
|
||||
let winptyCompatBundle = browserify(winptyCompatOptions)
|
||||
.external(path.join(outDir, 'Terminal.js'))
|
||||
.bundle()
|
||||
.pipe(source('./addons/winptyCompat/winptyCompat.js'))
|
||||
.pipe(buffer())
|
||||
.pipe(sourcemaps.init({loadMaps: true, sourceRoot: ''}))
|
||||
.pipe(sourcemaps.write('./'))
|
||||
.pipe(gulp.dest(buildDir));
|
||||
const addonBundle = browserify(addonOptions)
|
||||
.external(path.join(outDir, 'Terminal.js'))
|
||||
.bundle()
|
||||
.pipe(source(`./addons/${addon}/${addon}.js`))
|
||||
.pipe(buffer())
|
||||
.pipe(sourcemaps.init({loadMaps: true, sourceRoot: ''}))
|
||||
.pipe(sourcemaps.write('./'))
|
||||
.pipe(gulp.dest(buildDir));
|
||||
|
||||
// Copy all add-ons from outDir to buildDir
|
||||
let copyAddons = gulp.src([
|
||||
// Copy JS addons
|
||||
`${outDir}/addons/**/*`
|
||||
]).pipe(gulp.dest(`${buildDir}/addons`));
|
||||
return addonBundle;
|
||||
});
|
||||
|
||||
return merge(searchBundle, winptyCompatBundle, copyAddons);
|
||||
return merge(...bundles);
|
||||
});
|
||||
|
||||
gulp.task('instrument-test', function () {
|
||||
@@ -189,9 +179,11 @@ gulp.task('sorcery', ['browserify'], function () {
|
||||
});
|
||||
|
||||
gulp.task('sorcery-addons', ['browserify-addons'], function () {
|
||||
var chain = sorcery.loadSync(`${buildDir}/addons/search/search.js`);
|
||||
chain.apply();
|
||||
chain.writeSync();
|
||||
addons.forEach((addon) => {
|
||||
const chain = sorcery.loadSync(`${buildDir}/addons/${addon}/${addon}.js`);
|
||||
chain.apply();
|
||||
chain.writeSync();
|
||||
})
|
||||
});
|
||||
|
||||
gulp.task('webpack', ['build'], function() {
|
||||
|
||||
Generated
+251
-759
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -66,7 +66,7 @@
|
||||
"node-pty": "^0.7.2",
|
||||
"nodemon": "1.10.2",
|
||||
"sorcery": "^0.10.0",
|
||||
"tslint": "^4.0.2",
|
||||
"tslint": "^5.9.1",
|
||||
"typescript": "~2.4.0",
|
||||
"vinyl-buffer": "^1.0.0",
|
||||
"vinyl-source-stream": "^1.1.0",
|
||||
|
||||
+3
-3
@@ -3,19 +3,19 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { Charset } from './Types';
|
||||
import { ICharset } from './Interfaces';
|
||||
|
||||
/**
|
||||
* The character sets supported by the terminal. These enable several languages
|
||||
* to be represented within the terminal with only 8-bit encoding. See ISO 2022
|
||||
* for a discussion on character sets. Only VT100 character sets are supported.
|
||||
*/
|
||||
export const CHARSETS: { [key: string]: Charset } = {};
|
||||
export const CHARSETS: { [key: string]: ICharset } = {};
|
||||
|
||||
/**
|
||||
* The default character set, US.
|
||||
*/
|
||||
export const DEFAULT_CHARSET: Charset = CHARSETS['B'];
|
||||
export const DEFAULT_CHARSET: ICharset = CHARSETS['B'];
|
||||
|
||||
/**
|
||||
* DEC Special Character and Line Drawing Set.
|
||||
|
||||
@@ -217,7 +217,7 @@ export class CompositionHelper {
|
||||
if (!dontRecurse) {
|
||||
setTimeout(() => this.updateCompositionElements(true), 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the textarea's position so that the cursor does not blink on IE.
|
||||
@@ -226,5 +226,5 @@ export class CompositionHelper {
|
||||
private clearTextareaPosition(): void {
|
||||
this.textarea.style.left = '';
|
||||
this.textarea.style.top = '';
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,4 +76,4 @@ export namespace C0 {
|
||||
export const SP = '\x20';
|
||||
/** Delete (Caret = ^?) */
|
||||
export const DEL = '\x7f';
|
||||
};
|
||||
}
|
||||
|
||||
+7
-7
@@ -26,7 +26,7 @@ export class InputHandler implements IInputHandler {
|
||||
if (char >= ' ') {
|
||||
// calculate print space
|
||||
// expensive call, therefore we save width in line buffer
|
||||
const ch_width = wcwidth(code);
|
||||
const chWidth = wcwidth(code);
|
||||
|
||||
if (this._terminal.charset && this._terminal.charset[char]) {
|
||||
char = this._terminal.charset[char];
|
||||
@@ -36,7 +36,7 @@ export class InputHandler implements IInputHandler {
|
||||
|
||||
// insert combining char in last cell
|
||||
// FIXME: needs handling after cursor jumps
|
||||
if (!ch_width && this._terminal.buffer.x) {
|
||||
if (!chWidth && this._terminal.buffer.x) {
|
||||
// dont overflow left
|
||||
if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1]) {
|
||||
if (!this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][CHAR_DATA_WIDTH_INDEX]) {
|
||||
@@ -56,7 +56,7 @@ export class InputHandler implements IInputHandler {
|
||||
|
||||
// goto next line if ch would overflow
|
||||
// TODO: needs a global min terminal width of 2
|
||||
if (this._terminal.buffer.x + ch_width - 1 >= this._terminal.cols) {
|
||||
if (this._terminal.buffer.x + chWidth - 1 >= this._terminal.cols) {
|
||||
// autowrap - DECAWM
|
||||
if (this._terminal.wraparoundMode) {
|
||||
this._terminal.buffer.x = 0;
|
||||
@@ -70,7 +70,7 @@ export class InputHandler implements IInputHandler {
|
||||
(<any>this._terminal.buffer.lines.get(this._terminal.buffer.y)).isWrapped = true;
|
||||
}
|
||||
} else {
|
||||
if (ch_width === 2) // FIXME: check for xterm behavior
|
||||
if (chWidth === 2) // FIXME: check for xterm behavior
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ export class InputHandler implements IInputHandler {
|
||||
// insert mode: move characters to right
|
||||
if (this._terminal.insertMode) {
|
||||
// do this twice for a fullwidth char
|
||||
for (let moves = 0; moves < ch_width; ++moves) {
|
||||
for (let moves = 0; moves < chWidth; ++moves) {
|
||||
// remove last cell, if it's width is 0
|
||||
// we have to adjust the second last cell as well
|
||||
const removed = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).pop();
|
||||
@@ -94,12 +94,12 @@ export class InputHandler implements IInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
this._terminal.buffer.lines.get(row)[this._terminal.buffer.x] = [this._terminal.curAttr, char, ch_width, char.charCodeAt(0)];
|
||||
this._terminal.buffer.lines.get(row)[this._terminal.buffer.x] = [this._terminal.curAttr, char, chWidth, char.charCodeAt(0)];
|
||||
this._terminal.buffer.x++;
|
||||
this._terminal.updateRange(this._terminal.buffer.y);
|
||||
|
||||
// fullwidth char - set next cell width to zero and advance cursor
|
||||
if (ch_width === 2) {
|
||||
if (chWidth === 2) {
|
||||
this._terminal.buffer.lines.get(row)[this._terminal.buffer.x] = [this._terminal.curAttr, '', 0, undefined];
|
||||
this._terminal.buffer.x++;
|
||||
}
|
||||
|
||||
+27
-6
@@ -3,8 +3,8 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ILinkMatcherOptions } from './Interfaces';
|
||||
import { LinkMatcherHandler, LinkMatcherValidationCallback, Charset, LineData } from './Types';
|
||||
import { ICharset, ILinkMatcherOptions } from './Interfaces';
|
||||
import { LinkMatcherHandler, LinkMatcherValidationCallback, LineData } from './Types';
|
||||
import { IColorSet, IRenderer } from './renderer/Interfaces';
|
||||
import { IMouseZoneManager } from './input/Interfaces';
|
||||
|
||||
@@ -74,10 +74,10 @@ export interface IInputHandlingTerminal extends IEventEmitter {
|
||||
options: ITerminalOptions;
|
||||
cols: number;
|
||||
rows: number;
|
||||
charset: Charset;
|
||||
charset: ICharset;
|
||||
gcharset: number;
|
||||
glevel: number;
|
||||
charsets: Charset[];
|
||||
charsets: ICharset[];
|
||||
applicationKeypad: boolean;
|
||||
applicationCursor: boolean;
|
||||
originMode: boolean;
|
||||
@@ -116,7 +116,7 @@ export interface IInputHandlingTerminal extends IEventEmitter {
|
||||
blankLine(cur?: boolean, isWrapped?: boolean): LineData;
|
||||
is(term: string): boolean;
|
||||
send(data: string): void;
|
||||
setgCharset(g: number, charset: Charset): void;
|
||||
setgCharset(g: number, charset: ICharset): void;
|
||||
resize(x: number, y: number): void;
|
||||
log(text: string, data?: any): void;
|
||||
reset(): void;
|
||||
@@ -251,7 +251,7 @@ export interface IEventEmitter {
|
||||
export interface IListenerType {
|
||||
(data?: any): void;
|
||||
listener?: (data?: any) => void;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ILinkMatcherOptions {
|
||||
/**
|
||||
@@ -355,3 +355,24 @@ export interface ITheme {
|
||||
brightCyan?: string;
|
||||
brightWhite?: string;
|
||||
}
|
||||
|
||||
export interface ILinkMatcher {
|
||||
id: number;
|
||||
regex: RegExp;
|
||||
handler: LinkMatcherHandler;
|
||||
hoverTooltipCallback?: LinkMatcherHandler;
|
||||
hoverLeaveCallback?: () => void;
|
||||
matchIndex?: number;
|
||||
validationCallback?: LinkMatcherValidationCallback;
|
||||
priority?: number;
|
||||
}
|
||||
|
||||
export interface ICharset {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface ILinkHoverEvent {
|
||||
x: number;
|
||||
y: number;
|
||||
length: number;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
*/
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { ITerminal, ILinkifier, IBuffer, IBufferAccessor, IElementAccessor } from './Interfaces';
|
||||
import { ITerminal, ILinkifier, ILinkMatcher, IBuffer, IBufferAccessor, IElementAccessor } from './Interfaces';
|
||||
import { Linkifier } from './Linkifier';
|
||||
import { LinkMatcher, LineData } from './Types';
|
||||
import { LineData } from './Types';
|
||||
import { IMouseZoneManager, IMouseZone } from './input/Interfaces';
|
||||
import { MockBuffer } from './utils/TestUtils.test';
|
||||
import { CircularList } from './utils/CircularList';
|
||||
@@ -17,7 +17,7 @@ class TestLinkifier extends Linkifier {
|
||||
Linkifier.TIME_BEFORE_LINKIFY = 0;
|
||||
}
|
||||
|
||||
public get linkMatchers(): LinkMatcher[] { return this._linkMatchers; }
|
||||
public get linkMatchers(): ILinkMatcher[] { return this._linkMatchers; }
|
||||
public linkifyRows(): void { super.linkifyRows(0, this._terminal.buffer.lines.length - 1); }
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -3,8 +3,8 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ILinkMatcherOptions, ITerminal, IBufferAccessor, ILinkifier, IElementAccessor } from './Interfaces';
|
||||
import { LinkMatcher, LinkMatcherHandler, LinkMatcherValidationCallback, LineData, LinkHoverEvent, LinkHoverEventTypes } from './Types';
|
||||
import { ILinkHoverEvent, ILinkMatcher, ILinkMatcherOptions, ITerminal, IBufferAccessor, ILinkifier, IElementAccessor } from './Interfaces';
|
||||
import { LinkMatcherHandler, LinkMatcherValidationCallback, LineData, LinkHoverEventTypes } from './Types';
|
||||
import { IMouseZoneManager } from './input/Interfaces';
|
||||
import { MouseZone } from './input/MouseZoneManager';
|
||||
import { EventEmitter } from './EventEmitter';
|
||||
@@ -44,7 +44,7 @@ export class Linkifier extends EventEmitter implements ILinkifier {
|
||||
*/
|
||||
protected static TIME_BEFORE_LINKIFY = 200;
|
||||
|
||||
protected _linkMatchers: LinkMatcher[] = [];
|
||||
protected _linkMatchers: ILinkMatcher[] = [];
|
||||
|
||||
private _mouseZoneManager: IMouseZoneManager;
|
||||
private _rowsTimeoutId: number;
|
||||
@@ -143,7 +143,7 @@ export class Linkifier extends EventEmitter implements ILinkifier {
|
||||
if (this._nextLinkMatcherId !== HYPERTEXT_LINK_MATCHER_ID && !handler) {
|
||||
throw new Error('handler must be defined');
|
||||
}
|
||||
const matcher: LinkMatcher = {
|
||||
const matcher: ILinkMatcher = {
|
||||
id: this._nextLinkMatcherId++,
|
||||
regex,
|
||||
handler,
|
||||
@@ -163,7 +163,7 @@ export class Linkifier extends EventEmitter implements ILinkifier {
|
||||
* considered after older link matchers.
|
||||
* @param matcher The link matcher to be added.
|
||||
*/
|
||||
private _addLinkMatcherToList(matcher: LinkMatcher): void {
|
||||
private _addLinkMatcherToList(matcher: ILinkMatcher): void {
|
||||
if (this._linkMatchers.length === 0) {
|
||||
this._linkMatchers.push(matcher);
|
||||
return;
|
||||
@@ -219,7 +219,7 @@ export class Linkifier extends EventEmitter implements ILinkifier {
|
||||
* @param offset The how much of the row has already been linkified.
|
||||
* @return The link element(s) that were added.
|
||||
*/
|
||||
private _doLinkifyRow(rowIndex: number, text: string, matcher: LinkMatcher, offset: number = 0): void {
|
||||
private _doLinkifyRow(rowIndex: number, text: string, matcher: ILinkMatcher, offset: number = 0): void {
|
||||
// Iterate over nodes as we want to consider text nodes
|
||||
let result = [];
|
||||
const isHttpLinkMatcher = matcher.id === HYPERTEXT_LINK_MATCHER_ID;
|
||||
@@ -264,7 +264,7 @@ export class Linkifier extends EventEmitter implements ILinkifier {
|
||||
* @param uri The URI of the link.
|
||||
* @param matcher The link matcher for the link.
|
||||
*/
|
||||
private _addLink(x: number, y: number, uri: string, matcher: LinkMatcher): void {
|
||||
private _addLink(x: number, y: number, uri: string, matcher: ILinkMatcher): void {
|
||||
this._mouseZoneManager.add(new MouseZone(
|
||||
x + 1,
|
||||
x + 1 + uri.length,
|
||||
@@ -276,17 +276,17 @@ export class Linkifier extends EventEmitter implements ILinkifier {
|
||||
window.open(uri, '_blank');
|
||||
},
|
||||
e => {
|
||||
this.emit(LinkHoverEventTypes.HOVER, <LinkHoverEvent>{ x, y, length: uri.length});
|
||||
this.emit(LinkHoverEventTypes.HOVER, <ILinkHoverEvent>{ x, y, length: uri.length});
|
||||
this._terminal.element.style.cursor = 'pointer';
|
||||
},
|
||||
e => {
|
||||
this.emit(LinkHoverEventTypes.TOOLTIP, <LinkHoverEvent>{ x, y, length: uri.length});
|
||||
this.emit(LinkHoverEventTypes.TOOLTIP, <ILinkHoverEvent>{ x, y, length: uri.length});
|
||||
if (matcher.hoverTooltipCallback) {
|
||||
matcher.hoverTooltipCallback(e, uri);
|
||||
}
|
||||
},
|
||||
() => {
|
||||
this.emit(LinkHoverEventTypes.LEAVE, <LinkHoverEvent>{ x, y, length: uri.length});
|
||||
this.emit(LinkHoverEventTypes.LEAVE, <ILinkHoverEvent>{ x, y, length: uri.length});
|
||||
this._terminal.element.style.cursor = '';
|
||||
if (matcher.hoverLeaveCallback) {
|
||||
matcher.hoverLeaveCallback();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { assert } from 'chai';
|
||||
import { ITerminal } from './Interfaces';
|
||||
import { SelectionModel } from './SelectionModel';
|
||||
import {BufferSet} from './BufferSet';
|
||||
import { BufferSet } from './BufferSet';
|
||||
import { MockTerminal } from './utils/TestUtils.test';
|
||||
|
||||
class TestSelectionModel extends SelectionModel {
|
||||
|
||||
+18
-18
@@ -15,17 +15,17 @@ import { assert } from 'chai';
|
||||
import { Terminal } from './Terminal';
|
||||
import { CHAR_DATA_CHAR_INDEX } from './Buffer';
|
||||
|
||||
let primitive_pty: any;
|
||||
let primitivePty: any;
|
||||
|
||||
// fake sychronous pty write - read
|
||||
// we just pipe the data from slave to master as a child program would do
|
||||
// pty.js opens pipe fds with O_NONBLOCK
|
||||
// just wait 10ms instead of setting fds to blocking mode
|
||||
function ptyWriteRead(data: string, cb: (result: string) => void): void {
|
||||
fs.writeSync(primitive_pty.slave, data);
|
||||
fs.writeSync(primitivePty.slave, data);
|
||||
setTimeout(() => {
|
||||
let b = new Buffer(64000);
|
||||
let bytes = fs.readSync(primitive_pty.master, b, 0, 64000, null);
|
||||
let bytes = fs.readSync(primitivePty.master, b, 0, 64000, null);
|
||||
cb(b.toString('utf8', 0, bytes));
|
||||
});
|
||||
}
|
||||
@@ -37,7 +37,7 @@ function ptyReset(cb: (result: string) => void): void {
|
||||
|
||||
/* debug helpers */
|
||||
// generate colorful noisy output to compare xterm and emulator cell states
|
||||
function formatError(in_: string, out_: string, expected: string): string {
|
||||
function formatError(input: string, output: string, expected: string): string {
|
||||
function addLineNumber(start: number, color: string): (s: string) => string {
|
||||
let counter = start || 0;
|
||||
return function(s: string): string {
|
||||
@@ -47,9 +47,9 @@ function formatError(in_: string, out_: string, expected: string): string {
|
||||
}
|
||||
let line80 = '12345678901234567890123456789012345678901234567890123456789012345678901234567890';
|
||||
let s = '';
|
||||
s += '\n\x1b[34m' + JSON.stringify(in_);
|
||||
s += '\n\x1b[34m' + JSON.stringify(input);
|
||||
s += '\n\x1b[33m ' + line80 + '\n';
|
||||
s += out_.split('\n').map(addLineNumber(0, '\x1b[31m')).join('\n');
|
||||
s += output.split('\n').map(addLineNumber(0, '\x1b[31m')).join('\n');
|
||||
s += '\n\x1b[33m ' + line80 + '\n';
|
||||
s += expected.split('\n').map(addLineNumber(0, '\x1b[32m')).join('\n');
|
||||
return s;
|
||||
@@ -58,15 +58,15 @@ function formatError(in_: string, out_: string, expected: string): string {
|
||||
// simple debug output of terminal cells
|
||||
function terminalToString(term: Terminal): string {
|
||||
let result = '';
|
||||
let line_s = '';
|
||||
let lineText = '';
|
||||
for (let line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) {
|
||||
line_s = '';
|
||||
lineText = '';
|
||||
for (let cell = 0; cell < term.cols; ++cell) {
|
||||
line_s += term.buffer.lines.get(line)[cell][CHAR_DATA_CHAR_INDEX];
|
||||
lineText += term.buffer.lines.get(line)[cell][CHAR_DATA_CHAR_INDEX];
|
||||
}
|
||||
// rtrim empty cells as xterm does
|
||||
line_s = line_s.replace(/\s+$/, '');
|
||||
result += line_s;
|
||||
lineText = lineText.replace(/\s+$/, '');
|
||||
result += lineText;
|
||||
result += '\n';
|
||||
}
|
||||
return result;
|
||||
@@ -83,7 +83,7 @@ if (os.platform() !== 'win32') {
|
||||
/** some helpers for pty interaction */
|
||||
// we need a pty in between to get the termios decorations
|
||||
// for the basic test cases a raw pty device is enough
|
||||
primitive_pty = pty.native.open(COLS, ROWS);
|
||||
primitivePty = pty.native.open(COLS, ROWS);
|
||||
|
||||
/** tests */
|
||||
describe('xterm output comparison', () => {
|
||||
@@ -118,24 +118,24 @@ if (os.platform() !== 'win32') {
|
||||
((filename: string) => {
|
||||
it(filename.split('/').slice(-1)[0], done => {
|
||||
ptyReset(() => {
|
||||
let in_file = fs.readFileSync(filename, 'utf8');
|
||||
ptyWriteRead(in_file, from_pty => {
|
||||
let inFile = fs.readFileSync(filename, 'utf8');
|
||||
ptyWriteRead(inFile, fromPty => {
|
||||
// uncomment this to get log from terminal
|
||||
// console.log = function(){};
|
||||
|
||||
// Perform a synchronous .write(data)
|
||||
xterm.writeBuffer.push(from_pty);
|
||||
xterm.writeBuffer.push(fromPty);
|
||||
xterm.innerWrite();
|
||||
|
||||
let from_emulator = terminalToString(xterm);
|
||||
let fromEmulator = terminalToString(xterm);
|
||||
console.log = CONSOLE_LOG;
|
||||
let expected = fs.readFileSync(filename.split('.')[0] + '.text', 'utf8');
|
||||
// Some of the tests have whitespace on the right of lines, we trim all the linex
|
||||
// from xterm.js so ignore this for now at least.
|
||||
let expectedRightTrimmed = expected.split('\n').map(l => l.replace(/\s+$/, '')).join('\n');
|
||||
if (from_emulator !== expectedRightTrimmed) {
|
||||
if (fromEmulator !== expectedRightTrimmed) {
|
||||
// uncomment to get noisy output
|
||||
throw new Error(formatError(in_file, from_emulator, expected));
|
||||
throw new Error(formatError(inFile, fromEmulator, expected));
|
||||
// throw new Error('mismatch');
|
||||
}
|
||||
done();
|
||||
|
||||
+15
-12
@@ -38,9 +38,9 @@ import { CharMeasure } from './utils/CharMeasure';
|
||||
import * as Browser from './utils/Browser';
|
||||
import { MouseHelper } from './utils/MouseHelper';
|
||||
import { CHARSETS } from './Charsets';
|
||||
import { CustomKeyEventHandler, Charset, LinkMatcherHandler, LinkMatcherValidationCallback, CharData, LineData } from './Types';
|
||||
import { ITerminal, IBrowser, ITerminalOptions, IInputHandlingTerminal, ILinkMatcherOptions, IViewport, ICompositionHelper, ITheme, ILinkifier } from './Interfaces';
|
||||
import { BellSound } from './utils/Sounds';
|
||||
import { CustomKeyEventHandler, LinkMatcherHandler, LinkMatcherValidationCallback, CharData, LineData } from './Types';
|
||||
import { ITerminal, IBrowser, ICharset, ITerminalOptions, IInputHandlingTerminal, ILinkMatcherOptions, IViewport, ICompositionHelper, ITheme, ILinkifier } from './Interfaces';
|
||||
import { BELL_SOUND } from './utils/Sounds';
|
||||
import { DEFAULT_ANSI_COLORS } from './renderer/ColorManager';
|
||||
import { IMouseZoneManager } from './input/Interfaces';
|
||||
import { MouseZoneManager } from './input/MouseZoneManager';
|
||||
@@ -70,7 +70,7 @@ const DEFAULT_OPTIONS: ITerminalOptions = {
|
||||
termName: 'xterm',
|
||||
cursorBlink: false,
|
||||
cursorStyle: 'block',
|
||||
bellSound: BellSound,
|
||||
bellSound: BELL_SOUND,
|
||||
bellStyle: 'none',
|
||||
enableBold: true,
|
||||
fontFamily: 'courier-new, courier, monospace',
|
||||
@@ -132,10 +132,10 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
|
||||
// charset
|
||||
// The current charset
|
||||
public charset: Charset;
|
||||
public charset: ICharset;
|
||||
public gcharset: number;
|
||||
public glevel: number;
|
||||
public charsets: Charset[];
|
||||
public charsets: ICharset[];
|
||||
|
||||
// mouse properties
|
||||
private decLocator: boolean; // This is unstable and never set
|
||||
@@ -182,7 +182,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
private writeStopped: boolean;
|
||||
|
||||
// leftover surrogate high from previous write invocation
|
||||
private surrogate_high: string;
|
||||
private surrogateHigh: string;
|
||||
|
||||
// Store if user went browsing history in scrollback
|
||||
private userScrolling: boolean;
|
||||
@@ -281,7 +281,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
|
||||
this.xoffSentToCatchUp = false;
|
||||
this.writeStopped = false;
|
||||
this.surrogate_high = '';
|
||||
this.surrogateHigh = '';
|
||||
this.userScrolling = false;
|
||||
|
||||
this.inputHandler = new InputHandler(this);
|
||||
@@ -444,7 +444,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
this.element.classList.add('focus');
|
||||
this.showCursor();
|
||||
this.emit('focus');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Blur the terminal, calling the blur function on the terminal's underlying
|
||||
@@ -638,7 +638,6 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
this.on('resize', () => this.renderer.onResize(this.cols, this.rows, false));
|
||||
this.on('blur', () => this.renderer.onBlur());
|
||||
this.on('focus', () => this.renderer.onFocus());
|
||||
window.addEventListener('resize', () => this.renderer.onWindowResize(window.devicePixelRatio));
|
||||
this.charMeasure.on('charsizechanged', () => this.renderer.onResize(this.cols, this.rows, true));
|
||||
this.renderer.on('resize', (dimensions) => this.viewport.syncScrollArea());
|
||||
|
||||
@@ -1575,6 +1574,8 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
// page up
|
||||
if (ev.shiftKey) {
|
||||
result.scrollLines = -(this.rows - 1);
|
||||
} else if (modifiers) {
|
||||
result.key = C0.ESC + '[5;' + (modifiers + 1) + '~';
|
||||
} else {
|
||||
result.key = C0.ESC + '[5~';
|
||||
}
|
||||
@@ -1583,6 +1584,8 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
// page down
|
||||
if (ev.shiftKey) {
|
||||
result.scrollLines = this.rows - 1;
|
||||
} else if (modifiers) {
|
||||
result.key = C0.ESC + '[6;' + (modifiers + 1) + '~';
|
||||
} else {
|
||||
result.key = C0.ESC + '[6~';
|
||||
}
|
||||
@@ -1730,7 +1733,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
* @param g
|
||||
* @param charset
|
||||
*/
|
||||
public setgCharset(g: number, charset: Charset): void {
|
||||
public setgCharset(g: number, charset: ICharset): void {
|
||||
this.charsets[g] = charset;
|
||||
if (this.glevel === g) {
|
||||
this.charset = charset;
|
||||
@@ -2222,7 +2225,7 @@ function matchColorDistance(r1: number, g1: number, b1: number, r2: number, g2:
|
||||
return Math.pow(30 * (r1 - r2), 2)
|
||||
+ Math.pow(59 * (g1 - g2), 2)
|
||||
+ Math.pow(11 * (b1 - b2), 2);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function matchColor_(r1: number, g1: number, b1: number): number {
|
||||
|
||||
+1
-18
@@ -3,33 +3,16 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
export type LinkMatcher = {
|
||||
id: number,
|
||||
regex: RegExp,
|
||||
handler: LinkMatcherHandler,
|
||||
hoverTooltipCallback?: LinkMatcherHandler,
|
||||
hoverLeaveCallback?: () => void,
|
||||
matchIndex?: number,
|
||||
validationCallback?: LinkMatcherValidationCallback,
|
||||
priority?: number
|
||||
};
|
||||
export type LinkMatcherHandler = (event: MouseEvent, uri: string) => boolean | void;
|
||||
export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void;
|
||||
|
||||
export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
|
||||
export type Charset = {[key: string]: string};
|
||||
|
||||
export type CharData = [number, string, number, number];
|
||||
export type LineData = CharData[];
|
||||
|
||||
export type LinkHoverEvent = {
|
||||
x: number,
|
||||
y: number,
|
||||
length: number
|
||||
};
|
||||
|
||||
export enum LinkHoverEventTypes {
|
||||
HOVER = 'linkhover',
|
||||
TOOLTIP = 'linktooltip',
|
||||
LEAVE = 'linkleave'
|
||||
};
|
||||
}
|
||||
|
||||
+3
-3
@@ -119,7 +119,7 @@ export class Viewport implements IViewport {
|
||||
this.viewportElement.scrollTop += ev.deltaY * multiplier;
|
||||
// Prevent the page from scrolling when the terminal scrolls
|
||||
ev.preventDefault();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the touchstart event, recording the touch occurred.
|
||||
@@ -127,7 +127,7 @@ export class Viewport implements IViewport {
|
||||
*/
|
||||
public onTouchStart(ev: TouchEvent): void {
|
||||
this.lastTouchY = ev.touches[0].pageY;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the touchmove event, scrolling the viewport if the position shifted.
|
||||
@@ -141,5 +141,5 @@ export class Viewport implements IViewport {
|
||||
}
|
||||
this.viewportElement.scrollTop += deltaY;
|
||||
ev.preventDefault();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
|
||||
import * as attach from './attach'
|
||||
import * as attach from './attach';
|
||||
|
||||
class MockTerminal {}
|
||||
|
||||
|
||||
+20
-21
@@ -16,16 +16,16 @@
|
||||
* should happen instantly or at a maximum
|
||||
* frequency of 1 rendering per 10ms.
|
||||
*/
|
||||
export function attach(term, socket, bidirectional, buffered) {
|
||||
bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
|
||||
export function attach(term: any, socket: WebSocket, bidirectional: boolean, buffered: boolean): void {
|
||||
bidirectional = (typeof bidirectional === 'undefined') ? true : bidirectional;
|
||||
term.socket = socket;
|
||||
|
||||
term._flushBuffer = function() {
|
||||
term._flushBuffer = () => {
|
||||
term.write(term._attachSocketBuffer);
|
||||
term._attachSocketBuffer = null;
|
||||
};
|
||||
|
||||
term._pushToBuffer = function(data) {
|
||||
term._pushToBuffer = (data: string) => {
|
||||
if (term._attachSocketBuffer) {
|
||||
term._attachSocketBuffer += data;
|
||||
} else {
|
||||
@@ -34,20 +34,19 @@ export function attach(term, socket, bidirectional, buffered) {
|
||||
}
|
||||
};
|
||||
|
||||
var myTextDecoder;
|
||||
let myTextDecoder;
|
||||
|
||||
term._getMessage = function(ev) {
|
||||
var str;
|
||||
if (typeof ev.data === "object") {
|
||||
term._getMessage = function(ev: MessageEvent): void {
|
||||
let str;
|
||||
if (typeof ev.data === 'object') {
|
||||
if (ev.data instanceof ArrayBuffer) {
|
||||
if (!myTextDecoder) {
|
||||
myTextDecoder = new TextDecoder();
|
||||
}
|
||||
|
||||
str = myTextDecoder.decode( ev.data );
|
||||
}
|
||||
else {
|
||||
throw "TODO: handle Blob?";
|
||||
} else {
|
||||
throw 'TODO: handle Blob?';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +57,7 @@ export function attach(term, socket, bidirectional, buffered) {
|
||||
}
|
||||
};
|
||||
|
||||
term._sendData = function(data) {
|
||||
term._sendData = (data: string) => {
|
||||
if (socket.readyState !== 1) {
|
||||
return;
|
||||
}
|
||||
@@ -73,7 +72,7 @@ export function attach(term, socket, bidirectional, buffered) {
|
||||
|
||||
socket.addEventListener('close', term.detach.bind(term, socket));
|
||||
socket.addEventListener('error', term.detach.bind(term, socket));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the given terminal from the given socket
|
||||
@@ -82,20 +81,20 @@ export function attach(term, socket, bidirectional, buffered) {
|
||||
* @param {WebSocket} socket - The socket from which to detach the current
|
||||
* terminal.
|
||||
*/
|
||||
export function detach(term, socket) {
|
||||
export function detach(term: any, socket: WebSocket): void {
|
||||
term.off('data', term._sendData);
|
||||
|
||||
socket = (typeof socket == 'undefined') ? term.socket : socket;
|
||||
socket = (typeof socket === 'undefined') ? term.socket : socket;
|
||||
|
||||
if (socket) {
|
||||
socket.removeEventListener('message', term._getMessage);
|
||||
}
|
||||
|
||||
delete term.socket;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function apply(terminalConstructor) {
|
||||
export function apply(terminalConstructor: any): void {
|
||||
/**
|
||||
* Attaches the current terminal to the given socket
|
||||
*
|
||||
@@ -106,8 +105,8 @@ export function apply(terminalConstructor) {
|
||||
* should happen instantly or at a maximum
|
||||
* frequency of 1 rendering per 10ms.
|
||||
*/
|
||||
terminalConstructor.prototype.attach = function(socket, bidirectional, buffered) {
|
||||
return attach(this, socket, bidirectional, buffered);
|
||||
terminalConstructor.prototype.attach = function (socket: WebSocket, bidirectional: boolean, buffered: boolean): void {
|
||||
attach(this, socket, bidirectional, buffered);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -116,7 +115,7 @@ export function apply(terminalConstructor) {
|
||||
* @param {WebSocket} socket - The socket from which to detach the current
|
||||
* terminal.
|
||||
*/
|
||||
terminalConstructor.prototype.detach = function(socket) {
|
||||
return detach(this, socket);
|
||||
terminalConstructor.prototype.detach = function (socket: WebSocket): void {
|
||||
detach(this, socket);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"rootDir": ".",
|
||||
"outDir": "../../../lib/addons/attach/",
|
||||
"sourceMap": true,
|
||||
"removeComments": true
|
||||
"removeComments": true,
|
||||
"declaration": true
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user