mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge pull request #2397 from jerch/fix_function_identifier
fix stacking of multiple intermediates
This commit is contained in:
@@ -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)
|
||||
});
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user