Merge branch 'master' into mouse_services

This commit is contained in:
jerch
2019-09-05 20:59:11 +02:00
committed by GitHub
7 changed files with 116 additions and 2 deletions
+1
View File
@@ -7,6 +7,7 @@
},
"main": "lib/xterm-addon-attach.js",
"types": "typings/xterm-addon-attach.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"scripts": {
"build": "../../node_modules/.bin/tsc -p src",
+1
View File
@@ -7,6 +7,7 @@
},
"main": "lib/xterm-addon-fit.js",
"types": "typings/xterm-addon-fit.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"scripts": {
"build": "../../node_modules/.bin/tsc -p src",
+1
View File
@@ -7,6 +7,7 @@
},
"main": "lib/xterm-addon-search.js",
"types": "typings/xterm-addon-search.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"scripts": {
"build": "../../node_modules/.bin/tsc -p src",
@@ -7,6 +7,7 @@
},
"main": "lib/xterm-addon-web-links.js",
"types": "typings/xterm-addon-web-links.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"scripts": {
"build": "../../node_modules/.bin/tsc -p src",
+1
View File
@@ -7,6 +7,7 @@
},
"main": "lib/xterm-addon-webgl.js",
"types": "typings/xterm-addon-webgl.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"scripts": {
"build": "../../node_modules/.bin/tsc -p src",
+109 -1
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { IParsingState, IParams, ParamsArray, IOscParser, IOscHandler, OscFallbackHandlerType } from 'common/parser/Types';
import { IParsingState, IParams, ParamsArray, IOscParser, IOscHandler, OscFallbackHandlerType, IFunctionIdentifier } from 'common/parser/Types';
import { EscapeSequenceParser, TransitionTable, VT500_TRANSITION_TABLE } from 'common/parser/EscapeSequenceParser';
import * as chai from 'chai';
import { StringToUtf32, stringFromCodePoint, utf32ToString } from 'common/input/TextDecoder';
@@ -86,6 +86,9 @@ class TestEscapeSequenceParser extends EscapeSequenceParser {
public mockOscParser(): void {
this._oscParser = oscPutParser;
}
public identifier(id: IFunctionIdentifier): number {
return this._identifier(id);
}
}
// test object to collect parser actions and compare them with expected values
@@ -1552,5 +1555,110 @@ describe('EscapeSequenceParser', function (): void {
chai.expect(errorState).eql(null);
});
});
describe('function identifiers', () => {
describe('registration limits', () => {
it('prefix range 0x3c .. 0x3f, one byte', () => {
for (let i = 0x3c; i <= 0x3f; ++i) {
const c = String.fromCharCode(i);
chai.expect(parser.identToString(parser.identifier({prefix: c, final: 'z'}))).eql(c + 'z');
}
chai.assert.throws(() => { parser.identifier({prefix: '\x3b', final: 'z'}); }, 'prefix must be in range 0x3c .. 0x3f');
chai.assert.throws(() => { parser.identifier({prefix: '\x40', final: 'z'}); }, 'prefix must be in range 0x3c .. 0x3f');
chai.assert.throws(() => { parser.identifier({prefix: '??', final: 'z'}); }, 'only one byte as prefix supported');
});
it('intermediates range 0x20 .. 0x2f, up to two bytes', () => {
for (let i = 0x20; i <= 0x2f; ++i) {
const c = String.fromCharCode(i);
chai.expect(parser.identToString(parser.identifier({intermediates: c + c, final: 'z'}))).eql(c + c + 'z');
}
chai.assert.throws(() => { parser.identifier({intermediates: '\x1f', final: 'z'}); }, 'intermediate must be in range 0x20 .. 0x2f');
chai.assert.throws(() => { parser.identifier({intermediates: '\x30', final: 'z'}); }, 'intermediate must be in range 0x20 .. 0x2f');
chai.assert.throws(() => { parser.identifier({intermediates: '!!!', final: 'z'}); }, 'only two bytes as intermediates are supported');
});
it('final CSI/DCS range 0x40 .. 0x7e (default), one byte', () => {
for (let i = 0x40; i <= 0x7e; ++i) {
const c = String.fromCharCode(i);
chai.expect(parser.identToString(parser.identifier({final: c}))).eql(c);
}
chai.assert.throws(() => { parser.identifier({final: '\x3f'}); }, 'final must be in range 64 .. 126');
chai.assert.throws(() => { parser.identifier({final: '\x7f'}); }, 'final must be in range 64 .. 126');
chai.assert.throws(() => { parser.identifier({final: 'zz'}); }, 'final must be a single byte');
});
it('final ESC range 0x30 .. 0x7e, one byte', () => {
for (let i = 0x30; i <= 0x7e; ++i) {
const c = String.fromCharCode(i);
let handler: IDisposable | undefined;
chai.assert.doesNotThrow(() => { handler = parser.addEscHandler({final: c}, () => {}); }, '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');
});
it('id calculation - should stacking prefix -> intermediate -> final', () => {
chai.expect(parser.identToString(parser.identifier({final: 'z'}))).eql('z');
chai.expect(parser.identToString(parser.identifier({prefix: '?', final: 'z'}))).eql('?z');
chai.expect(parser.identToString(parser.identifier({intermediates: '!', final: 'z'}))).eql('!z');
chai.expect(parser.identToString(parser.identifier({prefix: '?', intermediates: '!', final: 'z'}))).eql('?!z');
chai.expect(parser.identToString(parser.identifier({prefix: '?', intermediates: '!!', final: 'z'}))).eql('?!!z');
});
});
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'); });
parse(parser, '\x1bz\x1b!z\x1b!!z');
h1.dispose();
h2.dispose();
h3.dispose();
parse(parser, '\x1bz\x1b!z\x1b!!z');
chai.expect(callstack).eql(['z', '!z', '!!z']);
});
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()]); });
parse(parser, '\x1b[1;z\x1b[1;!z\x1b[1;!!z\x1b[?1;z\x1b[?1;!z\x1b[?1;!!z');
h1.dispose();
h2.dispose();
h3.dispose();
h4.dispose();
h5.dispose();
h6.dispose();
parse(parser, '\x1b[1;z\x1b[1;!z\x1b[1;!!z\x1b[?1;z\x1b[?1;!z\x1b[?1;!!z');
chai.expect(callstack).eql([['z', [1, 0]], ['!z', [1, 0]], ['!!z', [1, 0]], ['?z', [1, 0]], ['?!z', [1, 0]], ['?!!z', [1, 0]]]);
});
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]); }));
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();
h3.dispose();
h4.dispose();
h5.dispose();
h6.dispose();
parse(parser, '\x1bP1;zAB\x1b\\\x1bP1;!zAB\x1b\\\x1bP1;!!zAB\x1b\\\x1bP?1;zAB\x1b\\\x1bP?1;!zAB\x1b\\\x1bP?1;!!zAB\x1b\\');
chai.expect(callstack).eql([
['z', [1, 0], 'AB'],
['!z', [1, 0], 'AB'],
['!!z', [1, 0], 'AB'],
['?z', [1, 0], 'AB'],
['?!z', [1, 0], 'AB'],
['?!!z', [1, 0], 'AB']
]);
});
});
});
// TODO: error conditions and error recovery (not implemented yet in parser)
});
+2 -1
View File
@@ -281,7 +281,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
this.setEscHandler({final: '\\'}, () => {});
}
private _identifier(id: IFunctionIdentifier, finalRange: number[] = [0x40, 0x7e]): number {
protected _identifier(id: IFunctionIdentifier, finalRange: number[] = [0x40, 0x7e]): number {
let res = 0;
if (id.prefix) {
if (id.prefix.length > 1) {
@@ -558,6 +558,7 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
i--;
break;
case ParserAction.COLLECT:
collect <<= 8;
collect |= code;
break;
case ParserAction.ESC_DISPATCH: