Merge branch 'master' into feature/xterm-addon-serialize-restore-layout-and-cursor

This commit is contained in:
Daniel Imms
2021-01-22 09:13:07 -08:00
committed by GitHub
48 changed files with 1158 additions and 2276 deletions
+1
View File
@@ -169,6 +169,7 @@ Xterm.js is used in several world-class applications to provide great terminal e
- [**TeleType**](https://github.com/akshaykmr/TeleType): cli tool that allows you to share your terminal online conveniently. Show off mad cli-fu, help a colleague, teach, or troubleshoot.
- [**Intervue**](https://www.intervue.io): Pair programming for interviews. Multiple programming languages supported, with results displayed by xterm.js.
- [**TRASA**](https://trasa.io): Zero trust access to Web, SSH, RDP and Database services.
- [**Commas**](https://github.com/CyanSalt/commas): Commas is a hackable terminal and command runner.
[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.
+1 -1
View File
@@ -31,7 +31,7 @@
],
"license": "MIT",
"dependencies": {
"font-finder": "^1.0.4",
"font-finder": "^1.1.0",
"font-ligatures": "^1.3.3"
},
"devDependencies": {
+16 -13
View File
@@ -1,16 +1,19 @@
{
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"sourceMap": true,
"outDir": "../out",
"rootDir": ".",
"strict": true,
"noUnusedLocals": true,
"preserveWatchOutput": true
},
"include": [
"./**/*",
"../../../typings/xterm.d.ts"
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"sourceMap": true,
"outDir": "../out",
"rootDir": ".",
"strict": true,
"noUnusedLocals": true,
"preserveWatchOutput": true,
"types": [
"../../../node_modules/@types/mocha"
]
},
"include": [
"./**/*",
"../../../typings/xterm.d.ts"
]
}
+9 -1
View File
@@ -78,13 +78,21 @@ follow-redirects@1.5.10:
dependencies:
debug "=3.1.0"
font-finder@^1.0.3, font-finder@^1.0.4:
font-finder@^1.0.3:
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-finder@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/font-finder/-/font-finder-1.1.0.tgz#2bff2b2762acba720239c8bec898a96daae90858"
integrity sha512-wpCL2uIbi6GurJbU7ZlQ3nGd61Ho+dSU6U83/xJT5UPFfN35EeCW/rOtS+5k+IuEZu2SYmHzDIPL9eA5tSYRAw==
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"
+9 -1
View File
@@ -158,8 +158,15 @@ export class SearchAddon implements ITerminalAddon {
};
if (incremental) {
// Try to expand selection to right first.
result = this._findInLine(term, searchPosition, searchOptions, false);
if (!(result && result.row === startRow && result.col === startCol)) {
const isOldResultHighlighted = result && result.row === startRow && result.col === startCol;
if (!isOldResultHighlighted) {
// If selection was not able to be expanded to the right, then try reverse search
if (currentSelection) {
searchPosition.startRow = currentSelection.endRow;
searchPosition.startCol = currentSelection.endColumn;
}
result = this._findInLine(term, searchPosition, searchOptions, true);
}
} else {
@@ -245,6 +252,7 @@ export class SearchAddon implements ITerminalAddon {
* @param term The search term.
* @param position The position to start the search.
* @param searchOptions Search options.
* @param isReverseSearch Whether the search should start from the right side of the terminal and search to the left.
* @return The search result if it was found.
*/
protected _findInLine(term: string, searchPosition: ISearchPosition, searchOptions: ISearchOptions = {}, isReverseSearch: boolean = false): ISearchResult | undefined {
+2 -2
View File
@@ -359,7 +359,7 @@ function empty(ar) {
}
function* parseMultiLineGen(filename, s) {
if (!~s.indexOf('@vt:')) {
if (!s.includes('@vt:')) {
return;
}
const lines = s.split('\n').map(el => el.trim().replace(/[*]/, '').replace(/\s/, ''));
@@ -413,7 +413,7 @@ function parseSingleLine(filename, s) {
const line = s.trim();
const match = line.match(REX_VT_LINE);
if (match !== null) {
if (!~TYPES.indexOf(match[2])) {
if (!TYPES.includes(match[2])) {
throw new Error(`unkown vt-command type "${match[2]}" specified in "${filename}"`);
}
return {
+2 -2
View File
@@ -25,7 +25,7 @@ if (fs.existsSync(addonsPath)) {
// walk all addon folders
fs.readdir(addonsPath, (err, files) => {
files.forEach(folder => {
for (const folder of files) {
const addonPath = path.join(addonsPath, folder);
// install only if there are dependencies listed
@@ -51,6 +51,6 @@ if (fs.existsSync(addonsPath)) {
} else {
console.log('Skipped', folder);
}
});
}
});
}
+5 -5
View File
@@ -11,7 +11,7 @@ const path = require('path');
// Setup auth
fs.writeFileSync(`${process.env['HOME']}/.npmrc`, `//registry.npmjs.org/:_authToken=${process.env['NPM_AUTH_TOKEN']}`);
const isDryRun = process.argv.indexOf('--dry') !== -1;
const isDryRun = process.argv.includes('--dry');
if (isDryRun) {
console.log('Publish dry run');
}
@@ -36,13 +36,13 @@ const addonPackageDirs = [
path.resolve(__dirname, '../addons/xterm-addon-webgl')
];
console.log(`Checking if addons need to be published`);
addonPackageDirs.forEach(p => {
for (const p of addonPackageDirs) {
const addon = path.basename(p);
if (changedFiles.some(e => e.indexOf(addon) !== -1)) {
if (changedFiles.some(e => e.includes(addon))) {
console.log(`Try publish ${addon}`);
checkAndPublishPackage(p);
}
});
}
// Publish website if it's a stable release
if (isStableRelease) {
@@ -54,7 +54,7 @@ function checkAndPublishPackage(packageDir) {
// Determine if this is a stable or beta release
const publishedVersions = getPublishedVersions(packageJson);
const isStableRelease = publishedVersions.indexOf(packageJson.version) === -1;
const isStableRelease = !publishedVersions.includes(packageJson.version);
// Get the next version
let nextVersion = isStableRelease ? packageJson.version : getNextBetaVersion(packageJson);
+5 -5
View File
@@ -88,19 +88,19 @@ function evalButtonCode(code) {
if (code & 64) {
button |= 4
}
let actionS = 'press';
let action = 'press';
let buttonS = reverseButtons[button];
if (button === 3) {
buttonS = '<none>';
actionS = 'release';
action = 'release';
}
if (move) {
actionS = 'move';
action = 'move';
} else if (4 <= button && button <= 7) {
buttonS = 'wheel';
actionS = button === 4 ? 'up' : button === 5 ? 'down' : button === 6 ? 'left' : 'right';
action = button === 4 ? 'up' : button === 5 ? 'down' : button === 6 ? 'left' : 'right';
}
return {button: buttonS, action: actionS, modifier};
return {button: buttonS, action, modifier};
}
// protocols
+1 -1
View File
@@ -93,7 +93,7 @@ const paddingElement = <HTMLInputElement>document.getElementById('padding');
function setPadding(): void {
term.element.style.padding = parseInt(paddingElement.value, 10).toString() + 'px';
term.fit();
addons.fit.instance.fit();
}
function getSearchOptions(e: KeyboardEvent): ISearchOptions {
+9 -9
View File
@@ -43,15 +43,15 @@ function startServer() {
const env = Object.assign({}, process.env);
env['COLORTERM'] = 'truecolor';
var cols = parseInt(req.query.cols),
rows = parseInt(req.query.rows),
term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], {
name: 'xterm-256color',
cols: cols || 80,
rows: rows || 24,
cwd: env.PWD,
env: env,
encoding: USE_BINARY ? null : 'utf8'
});
rows = parseInt(req.query.rows),
term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], {
name: 'xterm-256color',
cols: cols || 80,
rows: rows || 24,
cwd: process.platform === 'win32' ? undefined : env.PWD,
env: env,
encoding: USE_BINARY ? null : 'utf8'
});
console.log('Created terminal with PID: ' + term.pid);
terminals[term.pid] = term;
+4 -4
View File
@@ -52,17 +52,17 @@
"express-ws": "^4.0.0",
"glob": "^7.0.5",
"jsdom": "^16.4.0",
"mocha": "^8.2.0",
"mocha": "^8.2.1",
"mustache": "^4.0.1",
"node-pty": "^0.9.0",
"nyc": "^15.1.0",
"playwright": "^1.5.2",
"source-map-loader": "^1.1.2",
"ts-loader": "^8.0.7",
"ts-loader": "^8.0.8",
"typescript": "4.0",
"utf8": "^3.0.0",
"webpack": "^4.44.2",
"webpack-cli": "^4.1.0",
"webpack": "^5.4.0",
"webpack-cli": "^4.2.0",
"ws": "^7.3.1",
"xterm-benchmark": "^0.1.3"
}
+2 -1
View File
@@ -56,6 +56,7 @@ export class AccessibilityManager extends Disposable {
this._accessibilityTreeRoot.classList.add('xterm-accessibility');
this._rowContainer = document.createElement('div');
this._rowContainer.setAttribute('role', 'list');
this._rowContainer.classList.add('xterm-accessibility-tree');
this._rowElements = [];
for (let i = 0; i < this._terminal.rows; i++) {
@@ -262,7 +263,7 @@ export class AccessibilityManager extends Disposable {
const element = this._rowElements[i];
if (element) {
if (lineData.length === 0) {
element.innerHTML = '&nbsp;';
element.innerText = '\u00a0';
} else {
element.textContent = lineData;
}
+2 -2
View File
@@ -89,8 +89,8 @@ export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextA
export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, screenElement: HTMLElement, selectionService: ISelectionService, shouldSelectWord: boolean): void {
moveTextAreaUnderMouseCursor(ev, textarea, screenElement);
if (shouldSelectWord && !selectionService.isClickInSelection(ev)) {
selectionService.selectWordAtCursor(ev);
if (shouldSelectWord) {
selectionService.rightClickSelect(ev);
}
// Get textarea ready to copy from the context menu
+3 -4
View File
@@ -17,9 +17,8 @@ const DEFAULT_SELECTION = {
rgba: 0xFFFFFF4D
};
// An IIFE to generate DEFAULT_ANSI_COLORS. Do not mutate DEFAULT_ANSI_COLORS, instead make a copy
// and mutate that.
export const DEFAULT_ANSI_COLORS = (() => {
// An IIFE to generate DEFAULT_ANSI_COLORS.
export const DEFAULT_ANSI_COLORS = Object.freeze((() => {
const colors = [
// dark:
css.toColor('#2e3436'),
@@ -64,7 +63,7 @@ export const DEFAULT_ANSI_COLORS = (() => {
}
return colors;
})();
})());
/**
* Manages the source of truth for a terminal's colors.
+16 -1
View File
@@ -39,7 +39,7 @@ import { MouseZoneManager } from 'browser/MouseZoneManager';
import { AccessibilityManager } from './AccessibilityManager';
import { ITheme, IMarker, IDisposable, ISelectionPosition, ILinkProvider } from 'xterm';
import { DomRenderer } from 'browser/renderer/dom/DomRenderer';
import { IKeyboardEvent, KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions } from 'common/Types';
import { IKeyboardEvent, KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions, IAnsiColorChangeEvent } from 'common/Types';
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
@@ -53,6 +53,7 @@ import { Linkifier2 } from 'browser/Linkifier2';
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
import { CoreTerminal } from 'common/CoreTerminal';
import { ITerminalOptions as IInitializedTerminalOptions } from 'common/services/Services';
import { rgba } from 'browser/Color';
// Let it work inside Node.js for automated testing purposes.
const document: Document = (typeof window !== 'undefined') ? window.document : null as any;
@@ -148,6 +149,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.register(this._inputHandler.onRequestReset(() => this.reset()));
this.register(this._inputHandler.onRequestScroll((eraseAttr, isWrapped) => this.scroll(eraseAttr, isWrapped || undefined)));
this.register(this._inputHandler.onRequestWindowsOptionsReport(type => this._reportWindowsOptions(type)));
this.register(this._inputHandler.onAnsiColorChange((event) => this._changeAnsiColor(event)));
this.register(forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove));
this.register(forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange));
this.register(forwardEvent(this._inputHandler.onA11yChar, this._onA11yCharEmitter));
@@ -157,6 +159,19 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.register(this._bufferService.onResize(e => this._afterResize(e.cols, e.rows)));
}
private _changeAnsiColor(event: IAnsiColorChangeEvent): void {
if (!this._colorManager) { return; }
event.colors.forEach(ansiColor => {
const color = rgba.toColor(ansiColor.red, ansiColor.green, ansiColor.blue);
this._colorManager!.colors.ansi[ansiColor.colorIndex] = color;
});
this._renderService?.setColors(this._colorManager!.colors);
this.viewport?.onThemeChange(this._colorManager!.colors);
}
public dispose(): void {
if (this._isDisposed) {
return;
+2 -2
View File
@@ -32,7 +32,7 @@ if (os.platform() === 'darwin') {
);
}
// filter skipFilenames
const FILES = TESTFILES.filter(value => SKIP_FILES.indexOf(value.split('/').slice(-1)[0]) === -1);
const FILES = TESTFILES.filter(value => !SKIP_FILES.includes(value.split('/').slice(-1)[0]));
describe('Escape Sequence Files', function(): void {
this.timeout(1000);
@@ -104,7 +104,7 @@ describe('Escape Sequence Files', function(): void {
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 {
return (s: string): string => {
counter += 1;
return '\x1b[33m' + (' ' + counter).slice(-2) + color + s;
};
+14 -1
View File
@@ -35,6 +35,11 @@ export class CompositionHelper {
*/
private _isSendingComposition: boolean;
/**
* Data already sent due to keydown event.
*/
private _dataAlreadySent: string;
constructor(
private readonly _textarea: HTMLTextAreaElement,
private readonly _compositionView: HTMLElement,
@@ -46,6 +51,7 @@ export class CompositionHelper {
this._isComposing = false;
this._isSendingComposition = false;
this._compositionPosition = { start: 0, end: 0 };
this._dataAlreadySent = '';
}
/**
@@ -55,6 +61,7 @@ export class CompositionHelper {
this._isComposing = true;
this._compositionPosition.start = this._textarea.value.length;
this._compositionView.textContent = '';
this._dataAlreadySent = '';
this._compositionView.classList.add('active');
}
@@ -147,6 +154,9 @@ export class CompositionHelper {
if (this._isSendingComposition) {
this._isSendingComposition = false;
let input;
// Add length of data already sent due to keydown event,
// otherwise input characters can be duplicated. (Issue #3191)
currentCompositionPosition.start += this._dataAlreadySent.length;
if (this._isComposing) {
// Use the end position to get the string if a new composition has started.
input = this._textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end);
@@ -156,7 +166,9 @@ export class CompositionHelper {
// (eg. 2) after a composition character.
input = this._textarea.value.substring(currentCompositionPosition.start);
}
this._coreService.triggerDataEvent(input, true);
if (input.length > 0) {
this._coreService.triggerDataEvent(input, true);
}
}
}, 0);
}
@@ -176,6 +188,7 @@ export class CompositionHelper {
const newValue = this._textarea.value;
const diff = newValue.replace(oldValue, '');
if (diff.length > 0) {
this._dataAlreadySent = diff;
this._coreService.triggerDataEvent(diff, true);
}
}
+16 -11
View File
@@ -13,11 +13,13 @@ import * as Strings from '../LocalizableStrings';
import { IEvent, EventEmitter } from 'common/EventEmitter';
import { AddonManager } from './AddonManager';
import { IParams } from 'common/parser/Types';
import { BufferSet } from 'common/buffer/BufferSet';
export class Terminal implements ITerminalApi {
private _core: ITerminal;
private _addonManager: AddonManager;
private _parser: IParser | undefined;
private _buffer: BufferNamespaceApi | undefined;
constructor(options?: ITerminalOptions) {
this._core = new TerminalCore(options);
@@ -58,7 +60,10 @@ export class Terminal implements ITerminalApi {
public get cols(): number { return this._core.cols; }
public get buffer(): IBufferNamespaceApi {
this._checkProposedApi();
return new BufferNamespaceApi(this._core.buffers);
if (!this._buffer) {
this._buffer = new BufferNamespaceApi(this._core);
}
return this._buffer;
}
public get markers(): ReadonlyArray<IMarker> {
this._checkProposedApi();
@@ -170,7 +175,7 @@ export class Terminal implements ITerminalApi {
this._core.paste(data);
}
public getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'logLevel' | 'rendererType' | 'termName' | 'wordSeparator'): string;
public getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'visualBell'): boolean;
public getOption(key: 'allowTransparency' | 'altClickMovesCursor' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'visualBell'): boolean;
public getOption(key: 'cols' | 'fontSize' | 'letterSpacing' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback'): number;
public getOption(key: 'fontWeight' | 'fontWeightBold'): FontWeight;
public getOption(key: string): any;
@@ -182,7 +187,7 @@ export class Terminal implements ITerminalApi {
public setOption(key: 'logLevel', value: 'debug' | 'info' | 'warn' | 'error' | 'off'): void;
public setOption(key: 'bellStyle', value: 'none' | 'visual' | 'sound' | 'both'): void;
public setOption(key: 'cursorStyle', value: 'block' | 'underline' | 'bar'): void;
public setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'visualBell', value: boolean): void;
public setOption(key: 'allowTransparency' | 'altClickMovesCursor' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'visualBell', value: boolean): void;
public setOption(key: 'fontSize' | 'letterSpacing' | 'lineHeight' | 'tabStopWidth' | 'scrollback', value: number): void;
public setOption(key: 'theme', value: ITheme): void;
public setOption(key: 'cols' | 'rows', value: number): void;
@@ -245,21 +250,21 @@ class BufferNamespaceApi implements IBufferNamespaceApi {
private _onBufferChange = new EventEmitter<IBufferApi>();
public get onBufferChange(): IEvent<IBufferApi> { return this._onBufferChange.event; }
constructor(private _buffers: IBufferSet) {
this._normal = new BufferApiView(this._buffers.normal, 'normal');
this._alternate = new BufferApiView(this._buffers.alt, 'alternate');
this._buffers.onBufferActivate(() => this._onBufferChange.fire(this.active));
constructor(private _core: ITerminal) {
this._normal = new BufferApiView(this._core.buffers.normal, 'normal');
this._alternate = new BufferApiView(this._core.buffers.alt, 'alternate');
this._core.buffers.onBufferActivate(() => this._onBufferChange.fire(this.active));
}
public get active(): IBufferApi {
if (this._buffers.active === this._buffers.normal) { return this.normal; }
if (this._buffers.active === this._buffers.alt) { return this.alternate; }
if (this._core.buffers.active === this._core.buffers.normal) { return this.normal; }
if (this._core.buffers.active === this._core.buffers.alt) { return this.alternate; }
throw new Error('Active buffer is neither normal nor alternate');
}
public get normal(): IBufferApi {
return this._normal.init(this._buffers.normal);
return this._normal.init(this._core.buffers.normal);
}
public get alternate(): IBufferApi {
return this._alternate.init(this._buffers.alt);
return this._alternate.init(this._core.buffers.alt);
}
}
+1 -3
View File
@@ -16,9 +16,7 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number
cursor: undefined,
cursorAccent: undefined,
selection: undefined,
// For the static char atlas, we only use the first 16 colors, but we need all 256 for the
// dynamic character atlas.
ansi: colors.ansi.slice(0, 16)
ansi: colors.ansi
};
return {
devicePixelRatio: window.devicePixelRatio,
+4 -4
View File
@@ -138,7 +138,7 @@ export class DomRenderer extends Disposable implements IRenderer {
` width: ${this.dimensions.actualCellWidth}px` +
`}`;
this._dimensionsStyleElement.innerHTML = styles;
this._dimensionsStyleElement.textContent = styles;
this._selectionContainer.style.height = this._viewportElement.style.height;
this._screenElement.style.width = `${this.dimensions.canvasWidth}px`;
@@ -237,7 +237,7 @@ export class DomRenderer extends Disposable implements IRenderer {
`${this._terminalSelector} .${FG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { color: ${color.opaque(this._colors.background).css}; }` +
`${this._terminalSelector} .${BG_CLASS_PREFIX}${INVERTED_DEFAULT_COLOR} { background-color: ${this._colors.foreground.css}; }`;
this._themeStyleElement.innerHTML = styles;
this._themeStyleElement.textContent = styles;
}
public onDevicePixelRatioChange(): void {
@@ -348,7 +348,7 @@ export class DomRenderer extends Disposable implements IRenderer {
public clear(): void {
for (const e of this._rowElements) {
e.innerHTML = '';
e.innerText = '';
}
}
@@ -359,7 +359,7 @@ export class DomRenderer extends Disposable implements IRenderer {
for (let y = start; y <= end; y++) {
const rowElement = this._rowElements[y];
rowElement.innerHTML = '';
rowElement.innerText = '';
const row = y + this._bufferService.buffer.ydisp;
const lineData = this._bufferService.buffer.lines.get(row);
+2 -1
View File
@@ -14,6 +14,7 @@ export class CoreBrowserService implements ICoreBrowserService {
}
public get isFocused(): boolean {
return document.activeElement === this._textarea && document.hasFocus();
const docOrShadowRoot = this._textarea.getRootNode ? this._textarea.getRootNode() as Document | ShadowRoot : document;
return docOrShadowRoot.activeElement === this._textarea && document.hasFocus();
}
}
+47 -5
View File
@@ -105,6 +105,9 @@ export class SelectionService extends Disposable implements ISelectionService {
private _workCell: CellData = new CellData();
private _mouseDownTimeStamp: number = 0;
private _oldHasSelection: boolean = false;
private _oldSelectionStart: [number, number] | undefined = undefined;
private _oldSelectionEnd: [number, number] | undefined = undefined;
private _onLinuxMouseSelection = this.register(new EventEmitter<string>());
public get onLinuxMouseSelection(): IEvent<string> { return this._onLinuxMouseSelection.event; }
@@ -290,7 +293,7 @@ export class SelectionService extends Disposable implements ISelectionService {
* Checks if the current click was inside the current selection
* @param event The mouse event
*/
public isClickInSelection(event: MouseEvent): boolean {
private _isClickInSelection(event: MouseEvent): boolean {
const coords = this._getMouseBufferCoords(event);
const start = this._model.finalSelectionStart;
const end = this._model.finalSelectionEnd;
@@ -313,7 +316,7 @@ export class SelectionService extends Disposable implements ISelectionService {
* Selects word at the current mouse event coordinates.
* @param event The mouse event.
*/
public selectWordAtCursor(event: MouseEvent): void {
private _selectWordAtCursor(event: MouseEvent): void {
const coords = this._getMouseBufferCoords(event);
if (coords) {
this._selectWordAt(coords, false);
@@ -667,7 +670,7 @@ export class SelectionService extends Disposable implements ISelectionService {
this._removeMouseDownListeners();
if (this.selectionText.length <= 1 && timeElapsed < ALT_CLICK_MOVE_CURSOR_TIME && event.altKey) {
if (this.selectionText.length <= 1 && timeElapsed < ALT_CLICK_MOVE_CURSOR_TIME && event.altKey && this._optionsService.getOption('altClickMovesCursor')) {
if (this._bufferService.buffer.ybase === this._bufferService.buffer.ydisp) {
const coordinates = this._mouseService.getCoords(
event,
@@ -681,11 +684,43 @@ export class SelectionService extends Disposable implements ISelectionService {
this._coreService.triggerDataEvent(sequence, true);
}
}
} else if (this.hasSelection) {
this._onSelectionChange.fire();
} else {
this._fireEventIfSelectionChanged();
}
}
private _fireEventIfSelectionChanged(): void {
const start = this._model.finalSelectionStart;
const end = this._model.finalSelectionEnd;
const hasSelection = !!start && !!end && (start[0] !== end[0] || start[1] !== end[1]);
if (!hasSelection) {
if (this._oldHasSelection) {
this._fireOnSelectionChange(start, end, hasSelection);
}
return;
}
// Sanity check, these should not be undefined as there is a selection
if (!start || !end) {
return;
}
if (!this._oldSelectionStart || !this._oldSelectionEnd || (
start[0] !== this._oldSelectionStart[0] || start[1] !== this._oldSelectionStart[1] ||
end[0] !== this._oldSelectionEnd[0] || end[1] !== this._oldSelectionEnd[1])) {
this._fireOnSelectionChange(start, end, hasSelection);
}
}
private _fireOnSelectionChange(start: [number, number] | undefined, end: [number, number] | undefined, hasSelection: boolean): void {
this._oldSelectionStart = start;
this._oldSelectionEnd = end;
this._oldHasSelection = hasSelection;
this._onSelectionChange.fire();
}
private _onBufferActivate(e: {activeBuffer: IBuffer, inactiveBuffer: IBuffer}): void {
this.clearSelection();
// Only adjust the selection on trim, shiftElements is rarely used (only in
@@ -727,6 +762,13 @@ export class SelectionService extends Disposable implements ISelectionService {
this.refresh();
}
public rightClickSelect(ev: MouseEvent): void {
if (!this._isClickInSelection(ev)) {
this._selectWordAtCursor(ev);
this._fireEventIfSelectionChanged();
}
}
/**
* Gets positional information for the word at the coordinated specified.
* @param coords The coordinates to get the word at.
+1 -2
View File
@@ -91,8 +91,7 @@ export interface ISelectionService {
selectAll(): void;
selectLines(start: number, end: number): void;
clearSelection(): void;
isClickInSelection(event: MouseEvent): boolean;
selectWordAtCursor(event: MouseEvent): void;
rightClickSelect(event: MouseEvent): void;
shouldColumnSelect(event: KeyboardEvent | MouseEvent): boolean;
shouldForceSelection(event: MouseEvent): boolean;
refresh(isLinuxMouseSelection?: boolean): void;
+1
View File
@@ -4,6 +4,7 @@
"lib": [
"dom",
"es2015",
"es2016.Array.Include"
],
"outDir": "../../out",
"types": [
+53 -1
View File
@@ -5,7 +5,7 @@
import { assert, expect } from 'chai';
import { InputHandler } from 'common/InputHandler';
import { IBufferLine, IAttributeData } from 'common/Types';
import { IBufferLine, IAttributeData, IAnsiColorChangeEvent } from 'common/Types';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { CellData } from 'common/buffer/CellData';
import { Attributes, UnderlineStyle } from 'common/buffer/Constants';
@@ -40,6 +40,7 @@ class TestInputHandler extends InputHandler {
public get curAttrData(): IAttributeData { return (this as any)._curAttrData; }
public get windowTitleStack(): string[] { return this._windowTitleStack; }
public get iconNameStack(): string[] { return this._iconNameStack; }
public parseAnsiColorChange(data: string): IAnsiColorChangeEvent | null { return this._parseAnsiColorChange(data); }
}
describe('InputHandler', () => {
@@ -1692,4 +1693,55 @@ describe('InputHandler', () => {
assert.equal(coreService.decPrivateModes.origin, false);
});
});
describe('OSC', () => {
it('4: should parse correct Ansi color change data', () => {
// this is testing a private method
const event = inputHandler.parseAnsiColorChange('19;rgb:a1/b2/c3');
assert.isNotNull(event);
assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 });
}),
it('4: should ignore incorrect Ansi color change data', () => {
// this is testing a private method
assert.isNull(inputHandler.parseAnsiColorChange('17;rgb:a/b/c'));
assert.isNull(inputHandler.parseAnsiColorChange('17;rgb:#aabbcc'));
assert.isNull(inputHandler.parseAnsiColorChange('17;rgba:aa/bb/cc'));
assert.isNull(inputHandler.parseAnsiColorChange('rgb:aa/bb/cc'));
});
it('4: should parse a list of Ansi color changes', () => {
// this is testing a private method
const event = inputHandler.parseAnsiColorChange('19;rgb:a1/b2/c3;17;rgb:00/11/22;255;rgb:01/ef/2d');
assert.isNotNull(event);
assert.equal(event!.colors.length, 3);
assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 });
assert.deepEqual(event!.colors[1], { colorIndex: 17, red: 0x00, green: 0x11, blue: 0x22 });
assert.deepEqual(event!.colors[2], { colorIndex: 255, red: 0x01, green: 0xef, blue: 0x2d });
});
it('4: should ignore incorrect colors in a list of Ansi color changes', () => {
// this is testing a private method
const event = inputHandler.parseAnsiColorChange('19;rgb:a1/b2/c3;17;rgb:WR/ON/G;255;rgb:01/ef/2d');
assert.equal(event!.colors.length, 2);
assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 });
assert.deepEqual(event!.colors[1], { colorIndex: 255, red: 0x01, green: 0xef, blue: 0x2d });
});
it('4: should be case insensitive when parsing Ansi color changes', () => {
// this is testing a private method
const event = inputHandler.parseAnsiColorChange('19;rGb:A1/b2/C3');
assert.equal(event!.colors.length, 1);
assert.deepEqual(event!.colors[0], { colorIndex: 19, red: 0xa1, green: 0xb2, blue: 0xc3 });
});
it('4: should fire event on Ansi color change', (done) => {
inputHandler.onAnsiColorChange(e => {
assert.isNotNull(e);
assert.isNotNull(e!.colors);
assert.deepEqual(e!.colors[0], { colorIndex: 17, red: 0x1a, green: 0x2b, blue: 0x3c });
assert.deepEqual(e!.colors[1], { colorIndex: 12, red: 0x11, green: 0x22, blue: 0x33 });
done();
});
inputHandler.parse('\x1b]4;17;rgb:1a/2b/3c;12;rgb:11/22/33\x1b\\');
});
});
});
+286 -172
View File
File diff suppressed because it is too large Load Diff
+3 -12
View File
@@ -17,23 +17,14 @@ const isNode = (typeof navigator === 'undefined') ? true : false;
const userAgent = (isNode) ? 'node' : navigator.userAgent;
const platform = (isNode) ? 'node' : navigator.platform;
export const isFirefox = !!~userAgent.indexOf('Firefox');
export const isFirefox = userAgent.includes('Firefox');
export const isSafari = /^((?!chrome|android).)*safari/i.test(userAgent);
// Find the users platform. We use this to interpret the meta key
// and ISO third level shifts.
// http://stackoverflow.com/q/19877924/577598
export const isMac = contains(['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], platform);
export const isMac = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'].includes(platform);
export const isIpad = platform === 'iPad';
export const isIphone = platform === 'iPhone';
export const isWindows = contains(['Windows', 'Win16', 'Win32', 'WinCE'], platform);
export const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].includes(platform);
export const isLinux = platform.indexOf('Linux') >= 0;
/**
* Return if the given array contains the given element
* @param arr The array to search for the given element.
* @param el The element to look for into the array
*/
function contains(arr: any[], el: any): boolean {
return arr.indexOf(el) >= 0;
}
+15
View File
@@ -328,6 +328,20 @@ export interface IWindowOptions {
setWinLines?: boolean;
}
export interface IAnsiColorChangeEventColor {
colorIndex: number;
red: number;
green: number;
blue: number;
}
/**
* Event fired for OSC 4 command - to change ANSI color based on its index.
*/
export interface IAnsiColorChangeEvent {
colors: IAnsiColorChangeEventColor[];
}
/**
* Calls the parser and handles actions generated by the parser.
*/
@@ -389,6 +403,7 @@ export interface IInputHandler {
/** CSI ' ~ */ deleteColumns(params: IParams): void;
/** OSC 0
OSC 2 */ setTitle(data: string): void;
/** OSC 4 */ setAnsiColor(data: string): void;
/** ESC E */ nextLine(): void;
/** ESC = */ keypadApplicationMode(): void;
/** ESC > */ keypadNumericMode(): void;
+12 -12
View File
@@ -700,7 +700,7 @@ describe('Buffer', () => {
}
const wrappedLines: number[] = [];
for (let i = 0; i < buffer.lines.length; i++) {
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
}
});
});
@@ -723,7 +723,7 @@ describe('Buffer', () => {
}
const wrappedLines: number[] = [];
for (let i = 0; i < buffer.lines.length; i++) {
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
}
});
});
@@ -754,7 +754,7 @@ describe('Buffer', () => {
}
const wrappedLines: number[] = [];
for (let i = 0; i < buffer.lines.length; i++) {
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
}
});
});
@@ -777,7 +777,7 @@ describe('Buffer', () => {
}
const wrappedLines: number[] = [];
for (let i = 0; i < buffer.lines.length; i++) {
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
}
});
});
@@ -814,7 +814,7 @@ describe('Buffer', () => {
}
const wrappedLines: number[] = [];
for (let i = 0; i < buffer.lines.length; i++) {
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
}
});
});
@@ -837,7 +837,7 @@ describe('Buffer', () => {
}
const wrappedLines: number[] = [];
for (let i = 0; i < buffer.lines.length; i++) {
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
}
});
});
@@ -891,7 +891,7 @@ describe('Buffer', () => {
}
const wrappedLines = [1, 3, 5];
for (let i = 0; i < buffer.lines.length; i++) {
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
}
});
});
@@ -917,7 +917,7 @@ describe('Buffer', () => {
}
const wrappedLines = [1, 3, 5];
for (let i = 0; i < buffer.lines.length; i++) {
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
}
});
});
@@ -950,7 +950,7 @@ describe('Buffer', () => {
}
const wrappedLines = [11, 13, 15];
for (let i = 0; i < buffer.lines.length; i++) {
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
}
});
});
@@ -975,7 +975,7 @@ describe('Buffer', () => {
}
const wrappedLines = [11, 13, 15];
for (let i = 0; i < buffer.lines.length; i++) {
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
}
});
});
@@ -1014,7 +1014,7 @@ describe('Buffer', () => {
}
const wrappedLines = [8, 10, 12];
for (let i = 0; i < buffer.lines.length; i++) {
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
}
});
});
@@ -1040,7 +1040,7 @@ describe('Buffer', () => {
}
const wrappedLines = [8, 10, 12];
for (let i = 0; i < buffer.lines.length; i++) {
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.indexOf(i) !== -1, `line ${i} isWrapped must equal ${wrappedLines.indexOf(i) !== -1}`);
assert.equal(buffer.lines.get(i)!.isWrapped, wrappedLines.includes(i), `line ${i} isWrapped must equal ${wrappedLines.includes(i)}`);
}
});
});
+10 -8
View File
@@ -15,10 +15,9 @@ import { Disposable } from 'common/Lifecycle';
* provides also utilities for working with them.
*/
export class BufferSet extends Disposable implements IBufferSet {
private _normal: Buffer;
private _alt: Buffer;
private _activeBuffer: Buffer;
private _normal!: Buffer;
private _alt!: Buffer;
private _activeBuffer!: Buffer;
private _onBufferActivate = this.register(new EventEmitter<{activeBuffer: IBuffer, inactiveBuffer: IBuffer}>());
public get onBufferActivate(): IEvent<{activeBuffer: IBuffer, inactiveBuffer: IBuffer}> { return this._onBufferActivate.event; }
@@ -28,17 +27,20 @@ export class BufferSet extends Disposable implements IBufferSet {
* @param _terminal - The terminal the BufferSet will belong to
*/
constructor(
optionsService: IOptionsService,
bufferService: IBufferService
private readonly _optionsService: IOptionsService,
private readonly _bufferService: IBufferService
) {
super();
this.reset();
}
this._normal = new Buffer(true, optionsService, bufferService);
public reset(): void {
this._normal = new Buffer(true, this._optionsService, this._bufferService);
this._normal.fillViewportRows();
// The alt buffer should never have scrollback.
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
this._alt = new Buffer(false, optionsService, bufferService);
this._alt = new Buffer(false, this._optionsService, this._bufferService);
this._activeBuffer = this._normal;
this.setupTabStops();
+1
View File
@@ -56,6 +56,7 @@ export interface IBufferSet extends IDisposable {
activateNormalBuffer(): void;
activateAltBuffer(fillAttr?: IAttributeData): void;
reset(): void;
resize(newCols: number, newRows: number): void;
setupTabStops(i?: number): void;
}
+1 -1
View File
@@ -356,7 +356,7 @@ export function evaluateKeyboardEvent(
const keyCode = ev.ctrlKey ? ev.keyCode - 64 : ev.keyCode + 32;
result.key = C0.ESC + String.fromCharCode(keyCode);
}
} else if (isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) {
} else if (isMac && !ev.altKey && !ev.ctrlKey && !ev.shiftKey && ev.metaKey) {
if (ev.keyCode === 65) { // cmd + a
result.type = KeyboardResultType.SELECT_ALL;
}
+1 -1
View File
@@ -161,7 +161,7 @@ it('wcwidth should match all values from the old implementation', function(): vo
// ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766
// we are only interested in 2 LSBs, cut off higher bits
// ==> n = n & 3 e.g. 000000000000000000000000000000XX
return function (num: number): number {
return (num: number): number => {
num = num | 0; // get asm.js like optimization under V8
if (num < 32) {
return control | 0;
+18 -17
View File
@@ -61,11 +61,12 @@ class TestHandler implements IDcsHandler {
public put(data: Uint32Array, start: number, end: number): void {
this.output.push([this.msg, 'PUT', utf32ToString(data, start, end)]);
}
public unhook(success: boolean): void | boolean {
public unhook(success: boolean): boolean {
this.output.push([this.msg, 'UNHOOK', success]);
if (this.returnFalse) {
return false;
}
return true;
}
}
@@ -84,7 +85,7 @@ describe('DcsParser', () => {
});
describe('handler registration', () => {
it('setDcsHandler', () => {
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th'));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th'));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -100,7 +101,7 @@ describe('DcsParser', () => {
]);
});
it('clearDcsHandler', () => {
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th'));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th'));
parser.clearHandler(identifier({intermediates: '+', final: 'p'}));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
@@ -117,8 +118,8 @@ describe('DcsParser', () => {
]);
});
it('addDcsHandler', () => {
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
parser.addHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2'));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2'));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -137,8 +138,8 @@ describe('DcsParser', () => {
]);
});
it('addDcsHandler with return false', () => {
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
parser.addHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2', true));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2', true));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -157,8 +158,8 @@ describe('DcsParser', () => {
]);
});
it('dispose handlers', () => {
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
const dispo = parser.addHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2', true));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
const dispo = parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2', true));
dispo.dispose();
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
@@ -176,7 +177,7 @@ describe('DcsParser', () => {
});
describe('DcsHandlerFactory', () => {
it('should be called once on end(true)', () => {
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push([params.toArray(), data])));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push([params.toArray(), data]); return true; }));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -186,7 +187,7 @@ describe('DcsParser', () => {
assert.deepEqual(reports, [[[1, 2, 3], 'Here comes the mouse!']]);
});
it('should not be called on end(false)', () => {
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push([params.toArray(), data])));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push([params.toArray(), data]); return true; }));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -196,8 +197,8 @@ describe('DcsParser', () => {
assert.deepEqual(reports, []);
});
it('should be disposable', () => {
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push(['one', params.toArray(), data])));
const dispo = parser.addHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push(['two', params.toArray(), data])));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['one', params.toArray(), data]); return true; }));
const dispo = parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['two', params.toArray(), data]); return true; }));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -215,8 +216,8 @@ describe('DcsParser', () => {
assert.deepEqual(reports, [['two', [1, 2, 3], 'Here comes the mouse!'], ['one', [1, 2, 3], 'some other data']]);
});
it('should respect return false', () => {
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push(['one', params.toArray(), data])));
parser.addHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['two', params.toArray(), data]); return false; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['one', params.toArray(), data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['two', params.toArray(), data]); return false; }));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -227,7 +228,7 @@ describe('DcsParser', () => {
});
it('should work up to payload limit', function(): void {
this.timeout(10000);
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push([params.toArray(), data])));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push([params.toArray(), data]); return true; }));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
const data = toUtf32('A'.repeat(1000));
for (let i = 0; i < PAYLOAD_LIMIT; i += 1000) {
@@ -238,7 +239,7 @@ describe('DcsParser', () => {
});
it('should abort for payload limit +1', function(): void {
this.timeout(10000);
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push([params.toArray(), data])));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push([params.toArray(), data]); return true; }));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('A'.repeat(1000));
for (let i = 0; i < PAYLOAD_LIMIT; i += 1000) {
+18 -13
View File
@@ -20,9 +20,10 @@ export class DcsParser implements IDcsParser {
public dispose(): void {
this._handlers = Object.create(null);
this._handlerFb = () => {};
this._active = EMPTY_HANDLERS;
}
public addHandler(ident: number, handler: IDcsHandler): IDisposable {
public registerHandler(ident: number, handler: IDcsHandler): IDisposable {
if (this._handlers[ident] === undefined) {
this._handlers[ident] = [];
}
@@ -38,10 +39,6 @@ export class DcsParser implements IDcsParser {
};
}
public setHandler(ident: number, handler: IDcsHandler): void {
this._handlers[ident] = [handler];
}
public clearHandler(ident: number): void {
if (this._handlers[ident]) delete this._handlers[ident];
}
@@ -88,7 +85,7 @@ export class DcsParser implements IDcsParser {
} else {
let j = this._active.length - 1;
for (; j >= 0; j--) {
if (this._active[j].unhook(success) !== false) {
if (this._active[j].unhook(success)) {
break;
}
}
@@ -103,19 +100,27 @@ export class DcsParser implements IDcsParser {
}
}
// predefine empty params as [0] (ZDM)
const EMPTY_PARAMS = new Params();
EMPTY_PARAMS.addParam(0);
/**
* Convenient class to create a DCS handler from a single callback function.
* Note: The payload is currently limited to 50 MB (hardcoded).
*/
export class DcsHandler implements IDcsHandler {
private _data = '';
private _params: IParams | undefined;
private _params: IParams = EMPTY_PARAMS;
private _hitLimit: boolean = false;
constructor(private _handler: (data: string, params: IParams) => any) {}
constructor(private _handler: (data: string, params: IParams) => boolean) {}
public hook(params: IParams): void {
this._params = params.clone();
// since we need to preserve params until `unhook`, we have to clone it
// (only borrowed from parser and spans multiple parser states)
// perf optimization:
// clone only, if we have non empty params, otherwise stick with default
this._params = (params.length > 1 || params.params[0]) ? params.clone() : EMPTY_PARAMS;
this._data = '';
this._hitLimit = false;
}
@@ -131,14 +136,14 @@ export class DcsHandler implements IDcsHandler {
}
}
public unhook(success: boolean): any {
let ret;
public unhook(success: boolean): boolean {
let ret = false;
if (this._hitLimit) {
ret = false;
} else if (success) {
ret = this._handler(this._data, this._params || new Params());
ret = this._handler(this._data, this._params);
}
this._params = undefined;
this._params = EMPTY_PARAMS;
this._data = '';
this._hitLimit = false;
return ret;
+102 -95
View File
@@ -41,7 +41,7 @@ class MockOscPutParser implements IOscParser {
this._fallback(id, 'END', this.data.slice(this.data.indexOf(';') + 1));
}
}
public addHandler(ident: number, handler: IOscHandler): IDisposable {
public registerHandler(ident: number, handler: IOscHandler): IDisposable {
throw new Error('not implemented');
}
public setHandler(ident: number, handler: IOscHandler): void {
@@ -1206,11 +1206,13 @@ describe('EscapeSequenceParser', function (): void {
chai.expect(print).equal('');
});
it('ESC handler', function (): void {
parser2.setEscHandler({intermediates: '%', final: 'G'}, function (): void {
parser2.registerEscHandler({intermediates: '%', final: 'G'}, function (): boolean {
esc.push('%G');
return true;
});
parser2.setEscHandler({final: 'E'}, function (): void {
parser2.registerEscHandler({final: 'E'}, function (): boolean {
esc.push('E');
return true;
});
parse(parser2, INPUT);
chai.expect(esc).eql(['%G', 'E']);
@@ -1226,49 +1228,49 @@ describe('EscapeSequenceParser', function (): void {
});
describe('ESC custom handlers', () => {
it('prevent fallback', () => {
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); return true; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
parse(parser2, INPUT);
chai.expect(esc).eql(['custom - %G']);
});
it('allow fallback', () => {
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return false; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); return true; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return false; });
parse(parser2, INPUT);
chai.expect(esc).eql(['custom - %G', 'default - %G']);
});
it('Multiple custom handlers fallback once', () => {
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom2 - %G'); return false; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); return true; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom2 - %G'); return false; });
parse(parser2, INPUT);
chai.expect(esc).eql(['custom2 - %G', 'custom - %G']);
});
it('Multiple custom handlers no fallback', () => {
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom2 - %G'); return true; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); return true; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom2 - %G'); return true; });
parse(parser2, INPUT);
chai.expect(esc).eql(['custom2 - %G']);
});
it('Execution order should go from latest handler down to the original', () => {
const order: number[] = [];
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { order.push(1); });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { order.push(2); return false; });
parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { order.push(3); return false; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { order.push(1); return true; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { order.push(2); return false; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { order.push(3); return false; });
parse(parser2, '\x1b%G');
chai.expect(order).eql([3, 2, 1]);
});
it('Dispose should work', () => {
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
const dispo = parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); return true; });
const dispo = parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
dispo.dispose();
parse(parser2, INPUT);
chai.expect(esc).eql(['default - %G']);
});
it('Should not corrupt the parser when dispose is called twice', () => {
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
const dispo = parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); return true; });
const dispo = parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
dispo.dispose();
dispo.dispose();
parse(parser2, INPUT);
@@ -1276,8 +1278,9 @@ describe('EscapeSequenceParser', function (): void {
});
});
it('CSI handler', function (): void {
parser2.setCsiHandler({final: 'm'}, function (params: IParams): void {
parser2.registerCsiHandler({final: 'm'}, function (params: IParams): boolean {
csi.push(['m', params.toArray(), '']);
return true;
});
parse(parser2, INPUT);
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]);
@@ -1290,16 +1293,16 @@ describe('EscapeSequenceParser', function (): void {
describe('CSI custom handlers', () => {
it('Prevent fallback', () => {
const csiCustom: [string, ParamsArray, string][] = [];
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parse(parser2, INPUT);
chai.expect(csi).eql([], 'Should not fallback to original handler');
chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]);
});
it('Allow fallback', () => {
const csiCustom: [string, ParamsArray, string][] = [];
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return false; });
parser2.registerCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return false; });
parse(parser2, INPUT);
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']], 'Should fallback to original handler');
chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]);
@@ -1307,9 +1310,9 @@ describe('EscapeSequenceParser', function (): void {
it('Multiple custom handlers fallback once', () => {
const csiCustom: [string, ParamsArray, string][] = [];
const csiCustom2: [string, ParamsArray, string][] = [];
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.addCsiHandler({final: 'm'}, params => { csiCustom2.push(['m', params.toArray(), '']); return false; });
parser2.registerCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({final: 'm'}, params => { csiCustom2.push(['m', params.toArray(), '']); return false; });
parse(parser2, INPUT);
chai.expect(csi).eql([], 'Should not fallback to original handler');
chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]);
@@ -1318,9 +1321,9 @@ describe('EscapeSequenceParser', function (): void {
it('Multiple custom handlers no fallback', () => {
const csiCustom: [string, ParamsArray, string][] = [];
const csiCustom2: [string, ParamsArray, string][] = [];
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.addCsiHandler({final: 'm'}, params => { csiCustom2.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({final: 'm'}, params => { csiCustom2.push(['m', params.toArray(), '']); return true; });
parse(parser2, INPUT);
chai.expect(csi).eql([], 'Should not fallback to original handler');
chai.expect(csiCustom).eql([], 'Should not fallback once');
@@ -1328,16 +1331,16 @@ describe('EscapeSequenceParser', function (): void {
});
it('Execution order should go from latest handler down to the original', () => {
const order: number[] = [];
parser2.setCsiHandler({final: 'm'}, () => { order.push(1); });
parser2.addCsiHandler({final: 'm'}, () => { order.push(2); return false; });
parser2.addCsiHandler({final: 'm'}, () => { order.push(3); return false; });
parser2.registerCsiHandler({final: 'm'}, () => { order.push(1); return true; });
parser2.registerCsiHandler({final: 'm'}, () => { order.push(2); return false; });
parser2.registerCsiHandler({final: 'm'}, () => { order.push(3); return false; });
parse(parser2, '\x1b[0m');
chai.expect(order).eql([3, 2, 1]);
});
it('Dispose should work', () => {
const csiCustom: [string, ParamsArray, string][] = [];
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
const customHandler = parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); return true; });
const customHandler = parser2.registerCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
customHandler.dispose();
parse(parser2, INPUT);
chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]);
@@ -1345,8 +1348,8 @@ describe('EscapeSequenceParser', function (): void {
});
it('Should not corrupt the parser when dispose is called twice', () => {
const csiCustom: [string, ParamsArray, string][] = [];
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
const customHandler = parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); return true; });
const customHandler = parser2.registerCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
customHandler.dispose();
customHandler.dispose();
parse(parser2, INPUT);
@@ -1355,11 +1358,13 @@ describe('EscapeSequenceParser', function (): void {
});
});
it('EXECUTE handler', function (): void {
parser2.setExecuteHandler('\n', function (): void {
parser2.setExecuteHandler('\n', function (): boolean {
exe.push('\n');
return true;
});
parser2.setExecuteHandler('\r', function (): void {
parser2.setExecuteHandler('\r', function (): boolean {
exe.push('\r');
return true;
});
parse(parser2, INPUT);
chai.expect(exe).eql(['\r', '\n']);
@@ -1370,8 +1375,9 @@ describe('EscapeSequenceParser', function (): void {
chai.expect(exe).eql(['\n']);
});
it('OSC handler', function (): void {
parser2.setOscHandler(1, new OscHandler(function (data: string): void {
parser2.registerOscHandler(1, new OscHandler(function (data: string): boolean {
osc.push([1, data]);
return true;
}));
parse(parser2, INPUT);
chai.expect(osc).eql([[1, 'foo=bar']]);
@@ -1384,16 +1390,16 @@ describe('EscapeSequenceParser', function (): void {
describe('OSC custom handlers', () => {
it('Prevent fallback', () => {
const oscCustom: [number, string][] = [];
parser2.setOscHandler(1, new OscHandler(data => osc.push([1, data])));
parser2.addOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
parse(parser2, INPUT);
chai.expect(osc).eql([], 'Should not fallback to original handler');
chai.expect(oscCustom).eql([[1, 'foo=bar']]);
});
it('Allow fallback', () => {
const oscCustom: [number, string][] = [];
parser2.setOscHandler(1, new OscHandler(data => osc.push([1, data])));
parser2.addOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return false; }));
parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return false; }));
parse(parser2, INPUT);
chai.expect(osc).eql([[1, 'foo=bar']], 'Should fallback to original handler');
chai.expect(oscCustom).eql([[1, 'foo=bar']]);
@@ -1401,9 +1407,9 @@ describe('EscapeSequenceParser', function (): void {
it('Multiple custom handlers fallback once', () => {
const oscCustom: [number, string][] = [];
const oscCustom2: [number, string][] = [];
parser2.setOscHandler(1, new OscHandler(data => osc.push([1, data])));
parser2.addOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
parser2.addOscHandler(1, new OscHandler(data => { oscCustom2.push([1, data]); return false; }));
parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { oscCustom2.push([1, data]); return false; }));
parse(parser2, INPUT);
chai.expect(osc).eql([], 'Should not fallback to original handler');
chai.expect(oscCustom).eql([[1, 'foo=bar']]);
@@ -1412,9 +1418,9 @@ describe('EscapeSequenceParser', function (): void {
it('Multiple custom handlers no fallback', () => {
const oscCustom: [number, string][] = [];
const oscCustom2: [number, string][] = [];
parser2.setOscHandler(1, new OscHandler(data => osc.push([1, data])));
parser2.addOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
parser2.addOscHandler(1, new OscHandler(data => { oscCustom2.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { oscCustom2.push([1, data]); return true; }));
parse(parser2, INPUT);
chai.expect(osc).eql([], 'Should not fallback to original handler');
chai.expect(oscCustom).eql([], 'Should not fallback once');
@@ -1422,16 +1428,16 @@ describe('EscapeSequenceParser', function (): void {
});
it('Execution order should go from latest handler down to the original', () => {
const order: number[] = [];
parser2.setOscHandler(1, new OscHandler(() => order.push(1)));
parser2.addOscHandler(1, new OscHandler(() => { order.push(2); return false; }));
parser2.addOscHandler(1, new OscHandler(() => { order.push(3); return false; }));
parser2.registerOscHandler(1, new OscHandler(() => { order.push(1); return true; }));
parser2.registerOscHandler(1, new OscHandler(() => { order.push(2); return false; }));
parser2.registerOscHandler(1, new OscHandler(() => { order.push(3); return false; }));
parse(parser2, '\x1b]1;foo=bar\x1b\\');
chai.expect(order).eql([3, 2, 1]);
});
it('Dispose should work', () => {
const oscCustom: [number, string][] = [];
parser2.setOscHandler(1, new OscHandler(data => osc.push([1, data])));
const customHandler = parser2.addOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; }));
const customHandler = parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
customHandler.dispose();
parse(parser2, INPUT);
chai.expect(osc).eql([[1, 'foo=bar']]);
@@ -1439,8 +1445,8 @@ describe('EscapeSequenceParser', function (): void {
});
it('Should not corrupt the parser when dispose is called twice', () => {
const oscCustom: [number, string][] = [];
parser2.setOscHandler(1, new OscHandler(data => osc.push([1, data])));
const customHandler = parser2.addOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; }));
const customHandler = parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
customHandler.dispose();
customHandler.dispose();
parse(parser2, INPUT);
@@ -1449,7 +1455,7 @@ describe('EscapeSequenceParser', function (): void {
});
});
it('DCS handler', function (): void {
parser2.setDcsHandler({intermediates: '+', final: 'p'}, {
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, {
hook: function (params: IParams): void {
dcs.push(['hook', '', params.toArray(), 0]);
},
@@ -1460,8 +1466,9 @@ describe('EscapeSequenceParser', function (): void {
}
dcs.push(['put', s]);
},
unhook: function (): void {
unhook: function (): boolean {
dcs.push(['unhook']);
return true;
}
});
parse(parser2, '\x1bP1;2;3+pabc');
@@ -1482,54 +1489,54 @@ describe('EscapeSequenceParser', function (): void {
const DCS_INPUT = '\x1bP1;2;3+pabc\x1b\\';
it('Prevent fallback', () => {
const dcsCustom: [string, (number | number[])[], string][] = [];
parser2.setDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => dcsCustom.push(['A', params.toArray(), data])));
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
parse(parser2, DCS_INPUT);
chai.expect(dcsCustom).eql([['B', [1, 2, 3], 'abc']]);
});
it('Allow fallback', () => {
const dcsCustom: [string, (number | number[])[], string][] = [];
parser2.setDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => dcsCustom.push(['A', params.toArray(), data])));
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return false; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return false; }));
parse(parser2, DCS_INPUT);
chai.expect(dcsCustom).eql([['B', [1, 2, 3], 'abc'], ['A', [1, 2, 3], 'abc']]);
});
it('Multiple custom handlers fallback once', () => {
const dcsCustom: [string, (number | number[])[], string][] = [];
parser2.setDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => dcsCustom.push(['A', params.toArray(), data])));
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['C', params.toArray(), data]); return false; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['C', params.toArray(), data]); return false; }));
parse(parser2, DCS_INPUT);
chai.expect(dcsCustom).eql([['C', [1, 2, 3], 'abc'], ['B', [1, 2, 3], 'abc']]);
});
it('Multiple custom handlers no fallback', () => {
const dcsCustom: [string, (number | number[])[], string][] = [];
parser2.setDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => dcsCustom.push(['A', params.toArray(), data])));
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['C', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['C', params.toArray(), data]); return true; }));
parse(parser2, DCS_INPUT);
chai.expect(dcsCustom).eql([['C', [1, 2, 3], 'abc']]);
});
it('Execution order should go from latest handler down to the original', () => {
const order: number[] = [];
parser2.setDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler(() => order.push(1)));
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler(() => { order.push(2); return false; }));
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler(() => { order.push(3); return false; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler(() => { order.push(1); return true; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler(() => { order.push(2); return false; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler(() => { order.push(3); return false; }));
parse(parser2, DCS_INPUT);
chai.expect(order).eql([3, 2, 1]);
});
it('Dispose should work', () => {
const dcsCustom: [string, (number | number[])[], string][] = [];
parser2.setDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => dcsCustom.push(['A', params.toArray(), data])));
const dispo = parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; }));
const dispo = parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
dispo.dispose();
parse(parser2, DCS_INPUT);
chai.expect(dcsCustom).eql([['A', [1, 2, 3], 'abc']]);
});
it('Should not corrupt the parser when dispose is called twice', () => {
const dcsCustom: [string, (number | number[])[], string][] = [];
parser2.setDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => dcsCustom.push(['A', params.toArray(), data])));
const dispo = parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; }));
const dispo = parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
dispo.dispose();
dispo.dispose();
parse(parser2, DCS_INPUT);
@@ -1591,11 +1598,11 @@ describe('EscapeSequenceParser', function (): void {
for (let i = 0x30; i <= 0x7e; ++i) {
const final = String.fromCharCode(i);
let handler: IDisposable | undefined;
chai.assert.doesNotThrow(() => { handler = parser.addEscHandler({final}, () => {}); }, 'final must be in range 48 .. 126');
chai.assert.doesNotThrow(() => { handler = parser.registerEscHandler({final}, () => true); }, 'final must be in range 48 .. 126');
if (handler) handler.dispose();
}
chai.assert.throws(() => { parser.addEscHandler({final: '\x2f'}, () => {}); }, 'final must be in range 48 .. 126');
chai.assert.throws(() => { parser.addEscHandler({final: '\x7f'}, () => {}); }, 'final must be in range 48 .. 126');
chai.assert.throws(() => { parser.registerEscHandler({final: '\x2f'}, () => true); }, 'final must be in range 48 .. 126');
chai.assert.throws(() => { parser.registerEscHandler({final: '\x7f'}, () => true); }, 'final must be in range 48 .. 126');
});
it('id calculation - should stacking prefix -> intermediate -> final', () => {
chai.expect(parser.identToString(parser.identifier({final: 'z'}))).eql('z');
@@ -1608,9 +1615,9 @@ describe('EscapeSequenceParser', function (): void {
describe('identifier invocation', () => {
it('ESC', () => {
const callstack: string[] = [];
const h1 = parser.addEscHandler({final: 'z'}, () => { callstack.push('z'); });
const h2 = parser.addEscHandler({intermediates: '!', final: 'z'}, () => { callstack.push('!z'); });
const h3 = parser.addEscHandler({intermediates: '!!', final: 'z'}, () => { callstack.push('!!z'); });
const h1 = parser.registerEscHandler({final: 'z'}, () => { callstack.push('z'); return true; });
const h2 = parser.registerEscHandler({intermediates: '!', final: 'z'}, () => { callstack.push('!z'); return true; });
const h3 = parser.registerEscHandler({intermediates: '!!', final: 'z'}, () => { callstack.push('!!z'); return true; });
parse(parser, '\x1bz\x1b!z\x1b!!z');
h1.dispose();
h2.dispose();
@@ -1620,12 +1627,12 @@ describe('EscapeSequenceParser', function (): void {
});
it('CSI', () => {
const callstack: any[] = [];
const h1 = parser.addCsiHandler({final: 'z'}, params => { callstack.push(['z', params.toArray()]); });
const h2 = parser.addCsiHandler({intermediates: '!', final: 'z'}, params => { callstack.push(['!z', params.toArray()]); });
const h3 = parser.addCsiHandler({intermediates: '!!', final: 'z'}, params => { callstack.push(['!!z', params.toArray()]); });
const h4 = parser.addCsiHandler({prefix: '?', final: 'z'}, params => { callstack.push(['?z', params.toArray()]); });
const h5 = parser.addCsiHandler({prefix: '?', intermediates: '!', final: 'z'}, params => { callstack.push(['?!z', params.toArray()]); });
const h6 = parser.addCsiHandler({prefix: '?', intermediates: '!!', final: 'z'}, params => { callstack.push(['?!!z', params.toArray()]); });
const h1 = parser.registerCsiHandler({final: 'z'}, params => { callstack.push(['z', params.toArray()]); return true; });
const h2 = parser.registerCsiHandler({intermediates: '!', final: 'z'}, params => { callstack.push(['!z', params.toArray()]); return true; });
const h3 = parser.registerCsiHandler({intermediates: '!!', final: 'z'}, params => { callstack.push(['!!z', params.toArray()]); return true; });
const h4 = parser.registerCsiHandler({prefix: '?', final: 'z'}, params => { callstack.push(['?z', params.toArray()]); return true; });
const h5 = parser.registerCsiHandler({prefix: '?', intermediates: '!', final: 'z'}, params => { callstack.push(['?!z', params.toArray()]); return true; });
const h6 = parser.registerCsiHandler({prefix: '?', intermediates: '!!', final: 'z'}, params => { callstack.push(['?!!z', params.toArray()]); return true; });
parse(parser, '\x1b[1;z\x1b[1;!z\x1b[1;!!z\x1b[?1;z\x1b[?1;!z\x1b[?1;!!z');
h1.dispose();
h2.dispose();
@@ -1638,12 +1645,12 @@ describe('EscapeSequenceParser', function (): void {
});
it('DCS', () => {
const callstack: any[] = [];
const h1 = parser.addDcsHandler({final: 'z'}, new DcsHandler((data, params) => { callstack.push(['z', params.toArray(), data]); }));
const h2 = parser.addDcsHandler({intermediates: '!', final: 'z'}, new DcsHandler((data, params) => { callstack.push(['!z', params.toArray(), data]); }));
const h3 = parser.addDcsHandler({intermediates: '!!', final: 'z'}, new DcsHandler((data, params) => { callstack.push(['!!z', params.toArray(), data]); }));
const h4 = parser.addDcsHandler({prefix: '?', final: 'z'}, new DcsHandler((data, params) => { callstack.push(['?z', params.toArray(), data]); }));
const h5 = parser.addDcsHandler({prefix: '?', intermediates: '!', final: 'z'}, new DcsHandler((data, params) => { callstack.push(['?!z', params.toArray(), data]); }));
const h6 = parser.addDcsHandler({prefix: '?', intermediates: '!!', final: 'z'}, new DcsHandler((data, params) => { callstack.push(['?!!z', params.toArray(), data]); }));
const h1 = parser.registerDcsHandler({final: 'z'}, new DcsHandler((data, params) => { callstack.push(['z', params.toArray(), data]); return true; }));
const h2 = parser.registerDcsHandler({intermediates: '!', final: 'z'}, new DcsHandler((data, params) => { callstack.push(['!z', params.toArray(), data]); return true; }));
const h3 = parser.registerDcsHandler({intermediates: '!!', final: 'z'}, new DcsHandler((data, params) => { callstack.push(['!!z', params.toArray(), data]); return true; }));
const h4 = parser.registerDcsHandler({prefix: '?', final: 'z'}, new DcsHandler((data, params) => { callstack.push(['?z', params.toArray(), data]); return true; }));
const h5 = parser.registerDcsHandler({prefix: '?', intermediates: '!', final: 'z'}, new DcsHandler((data, params) => { callstack.push(['?!z', params.toArray(), data]); return true; }));
const h6 = parser.registerDcsHandler({prefix: '?', intermediates: '!!', final: 'z'}, new DcsHandler((data, params) => { callstack.push(['?!!z', params.toArray(), data]); return true; }));
parse(parser, '\x1bP1;zAB\x1b\\\x1bP1;!zAB\x1b\\\x1bP1;!!zAB\x1b\\\x1bP?1;zAB\x1b\\\x1bP?1;!zAB\x1b\\\x1bP?1;!!zAB\x1b\\');
h1.dispose();
h2.dispose();
+12 -24
View File
@@ -280,7 +280,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
this._errorHandler = this._errorHandlerFb;
// swallow 7bit ST (ESC+\)
this.setEscHandler({final: '\\'}, () => {});
this.registerEscHandler({final: '\\'}, () => true);
}
protected _identifier(id: IFunctionIdentifier, finalRange: number[] = [0x40, 0x7e]): number {
@@ -344,7 +344,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
this._printHandler = this._printHandlerFb;
}
public addEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): IDisposable {
public registerEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): IDisposable {
const ident = this._identifier(id, [0x30, 0x7e]);
if (this._escHandlers[ident] === undefined) {
this._escHandlers[ident] = [];
@@ -360,9 +360,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
}
};
}
public setEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): void {
this._escHandlers[this._identifier(id, [0x30, 0x7e])] = [handler];
}
public clearEscHandler(id: IFunctionIdentifier): void {
if (this._escHandlers[this._identifier(id, [0x30, 0x7e])]) delete this._escHandlers[this._identifier(id, [0x30, 0x7e])];
}
@@ -380,7 +377,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
this._executeHandlerFb = handler;
}
public addCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): IDisposable {
public registerCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): IDisposable {
const ident = this._identifier(id);
if (this._csiHandlers[ident] === undefined) {
this._csiHandlers[ident] = [];
@@ -396,9 +393,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
}
};
}
public setCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): void {
this._csiHandlers[this._identifier(id)] = [handler];
}
public clearCsiHandler(id: IFunctionIdentifier): void {
if (this._csiHandlers[this._identifier(id)]) delete this._csiHandlers[this._identifier(id)];
}
@@ -406,11 +400,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
this._csiHandlerFb = callback;
}
public addDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): IDisposable {
return this._dcsParser.addHandler(this._identifier(id), handler);
}
public setDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): void {
this._dcsParser.setHandler(this._identifier(id), handler);
public registerDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): IDisposable {
return this._dcsParser.registerHandler(this._identifier(id), handler);
}
public clearDcsHandler(id: IFunctionIdentifier): void {
this._dcsParser.clearHandler(this._identifier(id));
@@ -419,11 +410,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
this._dcsParser.setHandlerFallback(handler);
}
public addOscHandler(ident: number, handler: IOscHandler): IDisposable {
return this._oscParser.addHandler(ident, handler);
}
public setOscHandler(ident: number, handler: IOscHandler): void {
this._oscParser.setHandler(ident, handler);
public registerOscHandler(ident: number, handler: IOscHandler): IDisposable {
return this._oscParser.registerHandler(ident, handler);
}
public clearOscHandler(ident: number): void {
this._oscParser.clearHandler(ident);
@@ -533,8 +521,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
const handlers = this._csiHandlers[collect << 8 | code];
let j = handlers ? handlers.length - 1 : -1;
for (; j >= 0; j--) {
// undefined or true means success and to stop bubbling
if (handlers[j](params) !== false) {
// true means success and to stop bubbling
if (handlers[j](params)) {
break;
}
}
@@ -567,8 +555,8 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
const handlersEsc = this._escHandlers[collect << 8 | code];
let jj = handlersEsc ? handlersEsc.length - 1 : -1;
for (; jj >= 0; jj--) {
// undefined or true means success and to stop bubbling
if (handlersEsc[jj]() !== false) {
// true means success and to stop bubbling
if (handlersEsc[jj]()) {
break;
}
}
@@ -610,7 +598,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
case ParserAction.OSC_PUT:
// inner loop: 0x20 (SP) included, 0x7F (DEL) included
for (let j = i + 1; ; j++) {
if (j >= length || (code = data[j]) < 0x20 || (code > 0x7f && code <= 0x9f)) {
if (j >= length || (code = data[j]) < 0x20 || (code > 0x7f && code < NON_ASCII_PRINTABLE)) {
osc.put(data, i, j);
i = j - 1;
break;
+18 -17
View File
@@ -23,11 +23,12 @@ class TestHandler implements IOscHandler {
public put(data: Uint32Array, start: number, end: number): void {
this.output.push([this.msg, this.id, 'PUT', utf32ToString(data, start, end)]);
}
public end(success: boolean): void | boolean {
public end(success: boolean): boolean {
this.output.push([this.msg, this.id, 'END', success]);
if (this.returnFalse) {
return false;
}
return true;
}
}
@@ -78,7 +79,7 @@ describe('OscParser', () => {
});
describe('handler registration', () => {
it('setOscHandler', () => {
parser.setHandler(1234, new TestHandler(1234, reports, 'th'));
parser.registerHandler(1234, new TestHandler(1234, reports, 'th'));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -94,7 +95,7 @@ describe('OscParser', () => {
]);
});
it('clearOscHandler', () => {
parser.setHandler(1234, new TestHandler(1234, reports, 'th'));
parser.registerHandler(1234, new TestHandler(1234, reports, 'th'));
parser.clearHandler(1234);
parser.start();
let data = toUtf32('1234;Here comes');
@@ -111,8 +112,8 @@ describe('OscParser', () => {
]);
});
it('addOscHandler', () => {
parser.setHandler(1234, new TestHandler(1234, reports, 'th1'));
parser.addHandler(1234, new TestHandler(1234, reports, 'th2'));
parser.registerHandler(1234, new TestHandler(1234, reports, 'th1'));
parser.registerHandler(1234, new TestHandler(1234, reports, 'th2'));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -131,8 +132,8 @@ describe('OscParser', () => {
]);
});
it('addOscHandler with return false', () => {
parser.setHandler(1234, new TestHandler(1234, reports, 'th1'));
parser.addHandler(1234, new TestHandler(1234, reports, 'th2', true));
parser.registerHandler(1234, new TestHandler(1234, reports, 'th1'));
parser.registerHandler(1234, new TestHandler(1234, reports, 'th2', true));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -151,8 +152,8 @@ describe('OscParser', () => {
]);
});
it('dispose handlers', () => {
parser.setHandler(1234, new TestHandler(1234, reports, 'th1'));
const dispo = parser.addHandler(1234, new TestHandler(1234, reports, 'th2', true));
parser.registerHandler(1234, new TestHandler(1234, reports, 'th1'));
const dispo = parser.registerHandler(1234, new TestHandler(1234, reports, 'th2', true));
dispo.dispose();
parser.start();
let data = toUtf32('1234;Here comes');
@@ -170,7 +171,7 @@ describe('OscParser', () => {
});
describe('OscHandlerFactory', () => {
it('should be called once on end(true)', () => {
parser.setHandler(1234, new OscHandler(data => reports.push([1234, data])));
parser.registerHandler(1234, new OscHandler(data => { reports.push([1234, data]); return true; }));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -180,7 +181,7 @@ describe('OscParser', () => {
assert.deepEqual(reports, [[1234, 'Here comes the mouse!']]);
});
it('should not be called on end(false)', () => {
parser.setHandler(1234, new OscHandler(data => reports.push([1234, data])));
parser.registerHandler(1234, new OscHandler(data => { reports.push([1234, data]); return true; }));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -190,8 +191,8 @@ describe('OscParser', () => {
assert.deepEqual(reports, []);
});
it('should be disposable', () => {
parser.setHandler(1234, new OscHandler(data => reports.push(['one', data])));
const dispo = parser.addHandler(1234, new OscHandler(data => reports.push(['two', data])));
parser.registerHandler(1234, new OscHandler(data => { reports.push(['one', data]); return true; }));
const dispo = parser.registerHandler(1234, new OscHandler(data => { reports.push(['two', data]); return true; }));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -209,8 +210,8 @@ describe('OscParser', () => {
assert.deepEqual(reports, [['two', 'Here comes the mouse!'], ['one', 'some other data']]);
});
it('should respect return false', () => {
parser.setHandler(1234, new OscHandler(data => reports.push(['one', data])));
parser.addHandler(1234, new OscHandler(data => { reports.push(['two', data]); return false; }));
parser.registerHandler(1234, new OscHandler(data => { reports.push(['one', data]); return true; }));
parser.registerHandler(1234, new OscHandler(data => { reports.push(['two', data]); return false; }));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -221,7 +222,7 @@ describe('OscParser', () => {
});
it('should work up to payload limit', function(): void {
this.timeout(10000);
parser.setHandler(1234, new OscHandler(data => reports.push([1234, data])));
parser.registerHandler(1234, new OscHandler(data => { reports.push([1234, data]); return true; }));
parser.start();
let data = toUtf32('1234;');
parser.put(data, 0, data.length);
@@ -234,7 +235,7 @@ describe('OscParser', () => {
});
it('should abort for payload limit +1', function(): void {
this.timeout(10000);
parser.setHandler(1234, new OscHandler(data => reports.push([1234, data])));
parser.registerHandler(1234, new OscHandler(data => { reports.push([1234, data]); return true; }));
parser.start();
let data = toUtf32('1234;');
parser.put(data, 0, data.length);
+20 -21
View File
@@ -8,14 +8,16 @@ import { OscState, PAYLOAD_LIMIT } from 'common/parser/Constants';
import { utf32ToString } from 'common/input/TextDecoder';
import { IDisposable } from 'common/Types';
const EMPTY_HANDLERS: IOscHandler[] = [];
export class OscParser implements IOscParser {
private _state = OscState.START;
private _active = EMPTY_HANDLERS;
private _id = -1;
private _handlers: IHandlerCollection<IOscHandler> = Object.create(null);
private _handlerFb: OscFallbackHandlerType = () => { };
public addHandler(ident: number, handler: IOscHandler): IDisposable {
public registerHandler(ident: number, handler: IOscHandler): IDisposable {
if (this._handlers[ident] === undefined) {
this._handlers[ident] = [];
}
@@ -30,9 +32,6 @@ export class OscParser implements IOscParser {
}
};
}
public setHandler(ident: number, handler: IOscHandler): void {
this._handlers[ident] = [handler];
}
public clearHandler(ident: number): void {
if (this._handlers[ident]) delete this._handlers[ident];
}
@@ -43,6 +42,7 @@ export class OscParser implements IOscParser {
public dispose(): void {
this._handlers = Object.create(null);
this._handlerFb = () => {};
this._active = EMPTY_HANDLERS;
}
public reset(): void {
@@ -50,28 +50,28 @@ export class OscParser implements IOscParser {
if (this._state === OscState.PAYLOAD) {
this.end(false);
}
this._active = EMPTY_HANDLERS;
this._id = -1;
this._state = OscState.START;
}
private _start(): void {
const handlers = this._handlers[this._id];
if (!handlers) {
this._active = this._handlers[this._id] || EMPTY_HANDLERS;
if (!this._active.length) {
this._handlerFb(this._id, 'START');
} else {
for (let j = handlers.length - 1; j >= 0; j--) {
handlers[j].start();
for (let j = this._active.length - 1; j >= 0; j--) {
this._active[j].start();
}
}
}
private _put(data: Uint32Array, start: number, end: number): void {
const handlers = this._handlers[this._id];
if (!handlers) {
if (!this._active.length) {
this._handlerFb(this._id, 'PUT', utf32ToString(data, start, end));
} else {
for (let j = handlers.length - 1; j >= 0; j--) {
handlers[j].put(data, start, end);
for (let j = this._active.length - 1; j >= 0; j--) {
this._active[j].put(data, start, end);
}
}
}
@@ -80,20 +80,19 @@ export class OscParser implements IOscParser {
// other than the old code we always have to call .end
// to keep the bubbling we use `success` to indicate
// whether a handler should execute
const handlers = this._handlers[this._id];
if (!handlers) {
if (!this._active.length) {
this._handlerFb(this._id, 'END', success);
} else {
let j = handlers.length - 1;
let j = this._active.length - 1;
for (; j >= 0; j--) {
if (handlers[j].end(success) !== false) {
if (this._active[j].end(success)) {
break;
}
}
j--;
// cleanup left over handlers
for (; j >= 0; j--) {
handlers[j].end(false);
this._active[j].end(false);
}
}
}
@@ -101,7 +100,6 @@ export class OscParser implements IOscParser {
public start(): void {
// always reset leftover handlers
this.reset();
this._id = -1;
this._state = OscState.ID;
}
@@ -158,6 +156,7 @@ export class OscParser implements IOscParser {
}
this._end(success);
}
this._active = EMPTY_HANDLERS;
this._id = -1;
this._state = OscState.START;
}
@@ -171,7 +170,7 @@ export class OscHandler implements IOscHandler {
private _data = '';
private _hitLimit: boolean = false;
constructor(private _handler: (data: string) => any) {}
constructor(private _handler: (data: string) => boolean) {}
public start(): void {
this._data = '';
@@ -189,8 +188,8 @@ export class OscHandler implements IOscHandler {
}
}
public end(success: boolean): any {
let ret;
public end(success: boolean): boolean {
let ret = false;
if (this._hitLimit) {
ret = false;
} else if (success) {
+10 -15
View File
@@ -69,7 +69,7 @@ export interface IParsingState {
* CSI handler types.
* Note: `params` is borrowed.
*/
export type CsiHandlerType = (params: IParams) => boolean | void;
export type CsiHandlerType = (params: IParams) => boolean;
export type CsiFallbackHandlerType = (ident: number, params: IParams) => void;
/**
@@ -93,20 +93,20 @@ export interface IDcsHandler {
* execution of the command should depend on `success`.
* To save memory also cleanup data structures here.
*/
unhook(success: boolean): void | boolean;
unhook(success: boolean): boolean;
}
export type DcsFallbackHandlerType = (ident: number, action: 'HOOK' | 'PUT' | 'UNHOOK', payload?: any) => void;
/**
* ESC handler types.
*/
export type EscHandlerType = () => boolean | void;
export type EscHandlerType = () => boolean;
export type EscFallbackHandlerType = (identifier: number) => void;
/**
* EXECUTE handler types.
*/
export type ExecuteHandlerType = () => boolean | void;
export type ExecuteHandlerType = () => boolean;
export type ExecuteFallbackHandlerType = (ident: number) => void;
/**
@@ -129,7 +129,7 @@ export interface IOscHandler {
* execution of the command should depend on `success`.
* To save memory also cleanup data structures here.
*/
end(success: boolean): void | boolean;
end(success: boolean): boolean;
}
export type OscFallbackHandlerType = (ident: number, action: 'START' | 'PUT' | 'END', payload?: any) => void;
@@ -174,29 +174,25 @@ export interface IEscapeSequenceParser extends IDisposable {
setPrintHandler(handler: PrintHandlerType): void;
clearPrintHandler(): void;
setEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): void;
registerEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): IDisposable;
clearEscHandler(id: IFunctionIdentifier): void;
setEscHandlerFallback(handler: EscFallbackHandlerType): void;
addEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): IDisposable;
setExecuteHandler(flag: string, handler: ExecuteHandlerType): void;
clearExecuteHandler(flag: string): void;
setExecuteHandlerFallback(handler: ExecuteFallbackHandlerType): void;
setCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): void;
registerCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): IDisposable;
clearCsiHandler(id: IFunctionIdentifier): void;
setCsiHandlerFallback(callback: CsiFallbackHandlerType): void;
addCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): IDisposable;
setDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): void;
registerDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): IDisposable;
clearDcsHandler(id: IFunctionIdentifier): void;
setDcsHandlerFallback(handler: DcsFallbackHandlerType): void;
addDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): IDisposable;
setOscHandler(ident: number, handler: IOscHandler): void;
registerOscHandler(ident: number, handler: IOscHandler): IDisposable;
clearOscHandler(ident: number): void;
setOscHandlerFallback(handler: OscFallbackHandlerType): void;
addOscHandler(ident: number, handler: IOscHandler): IDisposable;
setErrorHandler(handler: (state: IParsingState) => IParsingState): void;
clearErrorHandler(): void;
@@ -209,8 +205,7 @@ export interface IEscapeSequenceParser extends IDisposable {
*/
export interface ISubParser<T, U> extends IDisposable {
reset(): void;
addHandler(ident: number, handler: T): IDisposable;
setHandler(ident: number, handler: T): void;
registerHandler(ident: number, handler: T): IDisposable;
clearHandler(ident: number): void;
setHandlerFallback(handler: U): void;
put(data: Uint32Array, start: number, end: number): void;
+1 -2
View File
@@ -49,8 +49,7 @@ export class BufferService extends Disposable implements IBufferService {
}
public reset(): void {
this.buffers.dispose();
this.buffers = new BufferSet(this._optionsService, this);
this.buffers.reset();
this.isUserScrolling = false;
}
}
+3 -3
View File
@@ -50,7 +50,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
windowOptions: {},
windowsMode: false,
wordSeparator: ' ()[]{}\',"`',
altClickMovesCursor: true,
convertEol: false,
termName: 'xterm',
cancelEvents: false
@@ -89,7 +89,7 @@ export class OptionsService implements IOptionsService {
if (!(key in DEFAULT_OPTIONS)) {
throw new Error('No option with key "' + key + '"');
}
if (CONSTRUCTOR_ONLY_OPTIONS.indexOf(key) !== -1) {
if (CONSTRUCTOR_ONLY_OPTIONS.includes(key)) {
throw new Error(`Option "${key}" can only be set in the constructor`);
}
if (this.options[key] === value) {
@@ -123,7 +123,7 @@ export class OptionsService implements IOptionsService {
// already valid numeric value
break;
}
value = FONT_WEIGHT_OPTIONS.indexOf(value) !== -1 ? value : DEFAULT_OPTIONS[key];
value = FONT_WEIGHT_OPTIONS.includes(value) ? value : DEFAULT_OPTIONS[key];
break;
case 'cursorWidth':
value = Math.floor(value);
+2
View File
@@ -184,6 +184,7 @@ export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off';
export type RendererType = 'dom' | 'canvas';
export interface IPartialTerminalOptions {
altClickMovesCursor?: boolean;
allowTransparency?: boolean;
bellSound?: string;
bellStyle?: 'none' | 'sound' /* | 'visual' | 'both' */;
@@ -220,6 +221,7 @@ export interface IPartialTerminalOptions {
export interface ITerminalOptions {
allowProposedApi: boolean;
allowTransparency: boolean;
altClickMovesCursor: boolean;
bellSound: string;
bellStyle: 'none' | 'sound' /* | 'visual' | 'both' */;
cols: number;
+2 -1
View File
@@ -2,7 +2,8 @@
"extends": "../tsconfig-library-base",
"compilerOptions": {
"lib": [
"es2015"
"es2015",
"es2016.Array.Include"
],
"outDir": "../../out",
"types": [
+157 -88
View File
@@ -6,9 +6,11 @@ import { perfContext, before, beforeEach, ThroughputRuntimeCase } from 'xterm-be
import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser';
import { C0, C1 } from 'common/data/EscapeSequences';
import { IDcsHandler, IParams } from 'common/parser/Types';
import { IDcsHandler, IOscHandler, IParams } from 'common/parser/Types';
import { OscHandler } from 'common/parser/OscParser';
import { DcsHandler } from '../../out/common/parser/DcsParser';
const SIZE = 5000000;
function toUtf32(s: string): Uint32Array {
const result = new Uint32Array(s.length);
@@ -18,10 +20,16 @@ function toUtf32(s: string): Uint32Array {
return result;
}
class DcsHandler implements IDcsHandler {
class FastDcsHandler implements IDcsHandler {
public hook(params: IParams): void {}
public put(data: Uint32Array, start: number, end: number): void {}
public unhook(): void {}
public unhook(success: boolean): boolean { return true; }
}
class FastOscHandler implements IOscHandler {
public start(): void {}
public put(data: Uint32Array, start: number, end: number): void {}
public end(success: boolean): boolean { return true; }
}
@@ -32,80 +40,81 @@ perfContext('Parser throughput - 50MB data', () => {
beforeEach(() => {
parser = new EscapeSequenceParser();
parser.setPrintHandler((data, start, end) => {});
parser.setCsiHandler({final: '@'}, params => {});
parser.setCsiHandler({final: 'A'}, params => {});
parser.setCsiHandler({final: 'B'}, params => {});
parser.setCsiHandler({final: 'C'}, params => {});
parser.setCsiHandler({final: 'D'}, params => {});
parser.setCsiHandler({final: 'E'}, params => {});
parser.setCsiHandler({final: 'F'}, params => {});
parser.setCsiHandler({final: 'G'}, params => {});
parser.setCsiHandler({final: 'H'}, params => {});
parser.setCsiHandler({final: 'I'}, params => {});
parser.setCsiHandler({final: 'J'}, params => {});
parser.setCsiHandler({final: 'K'}, params => {});
parser.setCsiHandler({final: 'L'}, params => {});
parser.setCsiHandler({final: 'M'}, params => {});
parser.setCsiHandler({final: 'P'}, params => {});
parser.setCsiHandler({final: 'S'}, params => {});
parser.setCsiHandler({final: 'T'}, params => {});
parser.setCsiHandler({final: 'X'}, params => {});
parser.setCsiHandler({final: 'Z'}, params => {});
parser.setCsiHandler({final: '`'}, params => {});
parser.setCsiHandler({final: 'a'}, params => {});
parser.setCsiHandler({final: 'b'}, params => {});
parser.setCsiHandler({final: 'c'}, params => {});
parser.setCsiHandler({final: 'd'}, params => {});
parser.setCsiHandler({final: 'e'}, params => {});
parser.setCsiHandler({final: 'f'}, params => {});
parser.setCsiHandler({final: 'g'}, params => {});
parser.setCsiHandler({final: 'h'}, params => {});
parser.setCsiHandler({final: 'l'}, params => {});
parser.setCsiHandler({final: 'm'}, params => {});
parser.setCsiHandler({final: 'n'}, params => {});
parser.setCsiHandler({final: 'p'}, params => {});
parser.setCsiHandler({final: 'q'}, params => {});
parser.setCsiHandler({final: 'r'}, params => {});
parser.setCsiHandler({final: 's'}, params => {});
parser.setCsiHandler({final: 'u'}, params => {});
parser.setExecuteHandler(C0.BEL, () => {});
parser.setExecuteHandler(C0.LF, () => {});
parser.setExecuteHandler(C0.VT, () => {});
parser.setExecuteHandler(C0.FF, () => {});
parser.setExecuteHandler(C0.CR, () => {});
parser.setExecuteHandler(C0.BS, () => {});
parser.setExecuteHandler(C0.HT, () => {});
parser.setExecuteHandler(C0.SO, () => {});
parser.setExecuteHandler(C0.SI, () => {});
parser.setExecuteHandler(C1.IND, () => {});
parser.setExecuteHandler(C1.NEL, () => {});
parser.setExecuteHandler(C1.HTS, () => {});
parser.setOscHandler(0, new OscHandler((data) => {}));
parser.setOscHandler(2, new OscHandler((data) => {}));
parser.setEscHandler({final: '7'}, () => {});
parser.setEscHandler({final: '8'}, () => {});
parser.setEscHandler({final: 'D'}, () => {});
parser.setEscHandler({final: 'E'}, () => {});
parser.setEscHandler({final: 'H'}, () => {});
parser.setEscHandler({final: 'M'}, () => {});
parser.setEscHandler({final: '='}, () => {});
parser.setEscHandler({final: '>'}, () => {});
parser.setEscHandler({final: 'c'}, () => {});
parser.setEscHandler({final: 'n'}, () => {});
parser.setEscHandler({final: 'o'}, () => {});
parser.setEscHandler({final: '|'}, () => {});
parser.setEscHandler({final: '}'}, () => {});
parser.setEscHandler({final: '~'}, () => {});
parser.setEscHandler({intermediates: '%', final: '@'}, () => {});
parser.setEscHandler({intermediates: '%', final: 'G'}, () => {});
parser.setDcsHandler({final: 'q'}, new DcsHandler());
parser.registerCsiHandler({final: '@'}, params => true);
parser.registerCsiHandler({final: 'A'}, params => true);
parser.registerCsiHandler({final: 'B'}, params => true);
parser.registerCsiHandler({final: 'C'}, params => true);
parser.registerCsiHandler({final: 'D'}, params => true);
parser.registerCsiHandler({final: 'E'}, params => true);
parser.registerCsiHandler({final: 'F'}, params => true);
parser.registerCsiHandler({final: 'G'}, params => true);
parser.registerCsiHandler({final: 'H'}, params => true);
parser.registerCsiHandler({final: 'I'}, params => true);
parser.registerCsiHandler({final: 'J'}, params => true);
parser.registerCsiHandler({final: 'K'}, params => true);
parser.registerCsiHandler({final: 'L'}, params => true);
parser.registerCsiHandler({final: 'M'}, params => true);
parser.registerCsiHandler({final: 'P'}, params => true);
parser.registerCsiHandler({final: 'S'}, params => true);
parser.registerCsiHandler({final: 'T'}, params => true);
parser.registerCsiHandler({final: 'X'}, params => true);
parser.registerCsiHandler({final: 'Z'}, params => true);
parser.registerCsiHandler({final: '`'}, params => true);
parser.registerCsiHandler({final: 'a'}, params => true);
parser.registerCsiHandler({final: 'b'}, params => true);
parser.registerCsiHandler({final: 'c'}, params => true);
parser.registerCsiHandler({final: 'd'}, params => true);
parser.registerCsiHandler({final: 'e'}, params => true);
parser.registerCsiHandler({final: 'f'}, params => true);
parser.registerCsiHandler({final: 'g'}, params => true);
parser.registerCsiHandler({final: 'h'}, params => true);
parser.registerCsiHandler({final: 'l'}, params => true);
parser.registerCsiHandler({final: 'm'}, params => true);
parser.registerCsiHandler({final: 'n'}, params => true);
parser.registerCsiHandler({final: 'p'}, params => true);
parser.registerCsiHandler({final: 'q'}, params => true);
parser.registerCsiHandler({final: 'r'}, params => true);
parser.registerCsiHandler({final: 's'}, params => true);
parser.registerCsiHandler({final: 'u'}, params => true);
parser.setExecuteHandler(C0.BEL, () => true);
parser.setExecuteHandler(C0.LF, () => true);
parser.setExecuteHandler(C0.VT, () => true);
parser.setExecuteHandler(C0.FF, () => true);
parser.setExecuteHandler(C0.CR, () => true);
parser.setExecuteHandler(C0.BS, () => true);
parser.setExecuteHandler(C0.HT, () => true);
parser.setExecuteHandler(C0.SO, () => true);
parser.setExecuteHandler(C0.SI, () => true);
parser.setExecuteHandler(C1.IND, () => true);
parser.setExecuteHandler(C1.NEL, () => true);
parser.setExecuteHandler(C1.HTS, () => true);
parser.registerOscHandler(0, new OscHandler(data => true));
parser.registerOscHandler(1, new FastOscHandler());
parser.registerEscHandler({final: '7'}, () => true);
parser.registerEscHandler({final: '8'}, () => true);
parser.registerEscHandler({final: 'D'}, () => true);
parser.registerEscHandler({final: 'E'}, () => true);
parser.registerEscHandler({final: 'H'}, () => true);
parser.registerEscHandler({final: 'M'}, () => true);
parser.registerEscHandler({final: '='}, () => true);
parser.registerEscHandler({final: '>'}, () => true);
parser.registerEscHandler({final: 'c'}, () => true);
parser.registerEscHandler({final: 'n'}, () => true);
parser.registerEscHandler({final: 'o'}, () => true);
parser.registerEscHandler({final: '|'}, () => true);
parser.registerEscHandler({final: '}'}, () => true);
parser.registerEscHandler({final: '~'}, () => true);
parser.registerEscHandler({intermediates: '%', final: '@'}, () => true);
parser.registerEscHandler({intermediates: '%', final: 'G'}, () => true);
parser.registerDcsHandler({final: 'p'}, new DcsHandler(data => true));
parser.registerDcsHandler({final: 'q'}, new FastDcsHandler());
});
perfContext('PRINT - a', () => {
before(() => {
const data = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
let content = '';
while (content.length < 50000000) {
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
@@ -120,7 +129,7 @@ perfContext('Parser throughput - 50MB data', () => {
before(() => {
const data = '\n\n\n\n\n\n\n';
let content = '';
while (content.length < 50000000) {
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
@@ -135,7 +144,7 @@ perfContext('Parser throughput - 50MB data', () => {
before(() => {
const data = '\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE\x1bE';
let content = '';
while (content.length < 50000000) {
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
@@ -150,7 +159,7 @@ perfContext('Parser throughput - 50MB data', () => {
before(() => {
const data = '\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G\x1b%G';
let content = '';
while (content.length < 50000000) {
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
@@ -165,7 +174,7 @@ perfContext('Parser throughput - 50MB data', () => {
before(() => {
const data = '\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A\x1b[A';
let content = '';
while (content.length < 50000000) {
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
@@ -180,7 +189,7 @@ perfContext('Parser throughput - 50MB data', () => {
before(() => {
const data = '\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p\x1b[?p';
let content = '';
while (content.length < 50000000) {
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
@@ -195,7 +204,7 @@ perfContext('Parser throughput - 50MB data', () => {
before(() => {
const data = '\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m\x1b[1;2m';
let content = '';
while (content.length < 50000000) {
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
@@ -210,7 +219,7 @@ perfContext('Parser throughput - 50MB data', () => {
before(() => {
const data = '\x1b[1;2;3;4;5;6;7;8;9;0m\x1b[1;2;3;4;5;6;7;8;9;0m\x1b[1;2;3;4;5;6;7;8;9;0m';
let content = '';
while (content.length < 50000000) {
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
@@ -221,11 +230,11 @@ perfContext('Parser throughput - 50MB data', () => {
}, {fork: true}).showAverageThroughput();
});
perfContext('OSC (short) - OSC 0;hi ST', () => {
perfContext('OSC string interface (short seq) - OSC 0;hi ST', () => {
before(() => {
const data = '\x1b]0;hi\x1b\\\x1b]0;hi\x1b\\\x1b]0;hi\x1b\\\x1b]0;hi\x1b\\x1b]0;hi\x1b\\';
let content = '';
while (content.length < 50000000) {
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
@@ -236,11 +245,11 @@ perfContext('Parser throughput - 50MB data', () => {
}, {fork: true}).showAverageThroughput();
});
perfContext('OSC (long) - OSC 0;<text> ST', () => {
perfContext('OSC string interface (long seq) - OSC 0;<text> ST', () => {
before(() => {
const data = '\x1b]0;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\x1b\\';
let content = '';
while (content.length < 50000000) {
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
@@ -251,11 +260,41 @@ perfContext('Parser throughput - 50MB data', () => {
}, {fork: true}).showAverageThroughput();
});
perfContext('DCS (short)', () => {
perfContext('OSC class interface (short seq) - OSC 0;hi ST', () => {
before(() => {
const data = '\x1bPq~~\x1b\\';
const data = '\x1b]1;hi\x1b\\\x1b]1;hi\x1b\\\x1b]1;hi\x1b\\\x1b]1;hi\x1b\\x1b]1;hi\x1b\\';
let content = '';
while (content.length < 50000000) {
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
});
new ThroughputRuntimeCase('', () => {
parser.parse(parsed, parsed.length);
return {payloadSize: parsed.length};
}, {fork: true}).showAverageThroughput();
});
perfContext('OSC class interface (long seq) - OSC 0;<text> ST', () => {
before(() => {
const data = '\x1b]1;Lorem ipsum dolor sit amet, consetetur sadipscing elitr.\x1b\\';
let content = '';
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
});
new ThroughputRuntimeCase('', () => {
parser.parse(parsed, parsed.length);
return {payloadSize: parsed.length};
}, {fork: true}).showAverageThroughput();
});
perfContext('DCS string interface (short seq)', () => {
before(() => {
const data = '\x1bPphi\x1b\\\x1bPphi\x1b\\\x1bPphi\x1b\\\x1bPphi\x1b\\\x1bPphi\x1b\\';
let content = '';
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
@@ -266,11 +305,41 @@ perfContext('Parser throughput - 50MB data', () => {
}, {fork: true}).showAverageThroughput();
});
perfContext('DCS (long)', () => {
perfContext('DCS string interface (long seq)', () => {
before(() => {
const data = '\x1bPq#0;2;0;0;0#1;2;100;100;0#2;2;0;100;0#1~~@@vv@@~~@@~~$#2??}}GG}}??}}??-#1!14@\x1b\\';
const data = '\x1bPpLorem ipsum dolor sit amet, consetetur sadipscing elitr.\x1b\\';
let content = '';
while (content.length < 50000000) {
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
});
new ThroughputRuntimeCase('', async () => {
parser.parse(parsed, parsed.length);
return {payloadSize: parsed.length};
}, {fork: true}).showAverageThroughput();
});
perfContext('DCS class interface (short seq)', () => {
before(() => {
const data = '\x1bPqhi\x1b\\\x1bPqhi\x1b\\\x1bPqhi\x1b\\\x1bPqhi\x1b\\\x1bPqhi\x1b\\';
let content = '';
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
});
new ThroughputRuntimeCase('', async () => {
parser.parse(parsed, parsed.length);
return {payloadSize: parsed.length};
}, {fork: true}).showAverageThroughput();
});
perfContext('DCS class interface (long seq)', () => {
before(() => {
const data = '\x1bPqLorem ipsum dolor sit amet, consetetur sadipscing elitr.\x1b\\';
let content = '';
while (content.length < SIZE) {
content += data;
}
parsed = toUtf32(content);
+6
View File
@@ -44,6 +44,12 @@ declare module 'xterm' {
*/
allowTransparency?: boolean;
/**
* If enabled, alt + click will move the prompt cursor to position
* underneath the mouse. The default is true.
*/
altClickMovesCursor?: boolean;
/**
* A data uri of the sound to use for the bell when `bellStyle = 'sound'`.
*/
+232 -1692
View File
File diff suppressed because it is too large Load Diff