Merge branch 'master' into left_right_scroll
This commit is contained in:
@@ -343,9 +343,12 @@ export class SearchAddon implements ITerminalAddon {
|
||||
return false;
|
||||
}
|
||||
terminal.select(result.col, result.row, result.term.length);
|
||||
let scroll = result.row - terminal.buffer.viewportY;
|
||||
scroll = scroll - Math.floor(terminal.rows / 2);
|
||||
terminal.scrollLines(scroll);
|
||||
// If it is not in the viewport then we scroll else it just gets selected
|
||||
if (result.row > (terminal.buffer.viewportY + terminal.rows) || result.row < terminal.buffer.viewportY) {
|
||||
let scroll = result.row - terminal.buffer.viewportY;
|
||||
scroll = scroll - Math.floor(terminal.rows / 2);
|
||||
terminal.scrollLines(scroll);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,6 @@ startServer();
|
||||
* For production builds see `webpack.config.js` in the root directory. If that is built the demo
|
||||
* can use that by switching out which `Terminal` is imported in `client.ts`, this is useful for
|
||||
* validating that the packaged version works correctly.
|
||||
*
|
||||
* The addons are not webpacked right now and are built directly to `lib/` via `tsc` as they are
|
||||
* the legacy format (`applyAddon`) and will be removed soon anyway.
|
||||
*/
|
||||
const clientConfig = {
|
||||
entry: path.resolve(__dirname, 'client.ts'),
|
||||
|
||||
@@ -27,5 +27,5 @@ r
|
||||
s
|
||||
t
|
||||
u[78Gvwxyz
|
||||
[r[24d
|
||||
[r[25d
|
||||
The end.
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
1
|
||||
2
|
||||
6
|
||||
7
|
||||
8
|
||||
9 ABC
|
||||
6
|
||||
7
|
||||
8
|
||||
9 ABC
|
||||
DEF
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
yz qrstu vwx
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
yz vwx
|
||||
The end.
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[3;7r1D2D3D4D5D6D7D8D9[78GABCDEF[8daDbDcDdDeDfDgDhDiDjDkDlDmDnDoDpDqDrDsDtDu[78Gvwxyz
|
||||
[r[24d
|
||||
[r[25d
|
||||
The end.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
2
|
||||
6
|
||||
7
|
||||
8
|
||||
@@ -21,5 +20,6 @@ DEF
|
||||
o
|
||||
p
|
||||
q
|
||||
The end. rstu vwx
|
||||
yz rstu vwx
|
||||
The end.
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
6 C
|
||||
8 ^^^^
|
||||
9 vvvv DL on line 11, expected: ACD_
|
||||
10 A
|
||||
@@ -13,14 +12,14 @@
|
||||
19 vvvv IL on line 21, expected: A_
|
||||
20 A
|
||||
|
||||
|
||||
22 ^^^^
|
||||
24 A
|
||||
25 B
|
||||
27 vvvv DL on line 28, expected: B_
|
||||
|
||||
23 vvvv IL on line 24, expected: _A
|
||||
25 B
|
||||
26 ^^^^
|
||||
28 A
|
||||
|
||||
29 B
|
||||
30 ^^^^
|
||||
31
|
||||
32
|
||||
32
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
u
|
||||
v
|
||||
w
|
||||
x[10;19r[25E1[24d[25F2[r[24d
|
||||
@@ -0,0 +1,25 @@
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
2
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
1
|
||||
t
|
||||
u
|
||||
v
|
||||
w
|
||||
x
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
u
|
||||
v
|
||||
w
|
||||
x[10;19r[H[25e1[r[24d
|
||||
@@ -0,0 +1,25 @@
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
u
|
||||
v
|
||||
w
|
||||
x
|
||||
1
|
||||
+36
-16
@@ -21,8 +21,8 @@ import { AttributeData } from 'common/buffer/AttributeData';
|
||||
import { IAttributeData, IDisposable } from 'common/Types';
|
||||
import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService } from 'common/services/Services';
|
||||
import { ISelectionService } from 'browser/services/Services';
|
||||
import { OscHandlerFactory } from 'common/parser/OscParser';
|
||||
import { DcsHandlerFactory } from 'common/parser/DcsParser';
|
||||
import { OscHandler } from 'common/parser/OscParser';
|
||||
import { DcsHandler } from 'common/parser/DcsParser';
|
||||
|
||||
/**
|
||||
* Map collect to glevel. Used in `selectCharset`.
|
||||
@@ -245,10 +245,10 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* OSC handler
|
||||
*/
|
||||
// 0 - icon name + title
|
||||
this._parser.setOscHandler(0, new OscHandlerFactory((data: string) => this.setTitle(data)));
|
||||
this._parser.setOscHandler(0, new OscHandler((data: string) => this.setTitle(data)));
|
||||
// 1 - icon name
|
||||
// 2 - title
|
||||
this._parser.setOscHandler(2, new OscHandlerFactory((data: string) => this.setTitle(data)));
|
||||
this._parser.setOscHandler(2, new OscHandler((data: string) => this.setTitle(data)));
|
||||
// 3 - set property X in the form "prop=value"
|
||||
// 4 - Change Color Number
|
||||
// 5 - Change Special Color Number
|
||||
@@ -433,10 +433,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
if (wraparoundMode) {
|
||||
buffer.x = 0;
|
||||
buffer.y++;
|
||||
if (buffer.y > buffer.scrollBottom) {
|
||||
if (buffer.y === buffer.scrollBottom + 1) {
|
||||
buffer.y--;
|
||||
this._terminal.scroll(true);
|
||||
} else {
|
||||
if (buffer.y >= this._bufferService.rows) {
|
||||
buffer.y = this._bufferService.rows - 1;
|
||||
}
|
||||
// The line already exists (eg. the initial viewport), mark it as a
|
||||
// wrapped line
|
||||
buffer.lines.get(buffer.y).isWrapped = true;
|
||||
@@ -506,7 +509,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Forward addDcsHandler from parser.
|
||||
*/
|
||||
public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable {
|
||||
return this._parser.addDcsHandler(id, new DcsHandlerFactory(callback));
|
||||
return this._parser.addDcsHandler(id, new DcsHandler(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -520,7 +523,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Forward addOscHandler from parser.
|
||||
*/
|
||||
public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
|
||||
return this._parser.addOscHandler(ident, new OscHandlerFactory(callback));
|
||||
return this._parser.addOscHandler(ident, new OscHandler(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -543,9 +546,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
buffer.x = 0;
|
||||
}
|
||||
buffer.y++;
|
||||
if (buffer.y > buffer.scrollBottom) {
|
||||
if (buffer.y === buffer.scrollBottom + 1) {
|
||||
buffer.y--;
|
||||
this._terminal.scroll();
|
||||
} else if (buffer.y >= this._bufferService.rows) {
|
||||
buffer.y = this._bufferService.rows - 1;
|
||||
}
|
||||
// If the end of the line is hit, prevent this action from wrapping around to the next line.
|
||||
if (buffer.x >= this._bufferService.cols) {
|
||||
@@ -646,7 +651,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Cursor Up Ps Times (default = 1) (CUU).
|
||||
*/
|
||||
public cursorUp(params: IParams): void {
|
||||
this._moveCursor(0, -(params.params[0] || 1));
|
||||
// stop at scrollTop
|
||||
const diffToTop = this._bufferService.buffer.y - this._bufferService.buffer.scrollTop;
|
||||
if (diffToTop >= 0) {
|
||||
this._moveCursor(0, -Math.min(diffToTop, params.params[0] || 1));
|
||||
} else {
|
||||
this._moveCursor(0, -(params.params[0] || 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -654,7 +665,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Cursor Down Ps Times (default = 1) (CUD).
|
||||
*/
|
||||
public cursorDown(params: IParams): void {
|
||||
this._moveCursor(0, params.params[0] || 1);
|
||||
// stop at scrollBottom
|
||||
const diffToBottom = this._bufferService.buffer.scrollBottom - this._bufferService.buffer.y;
|
||||
if (diffToBottom >= 0) {
|
||||
this._moveCursor(0, Math.min(diffToBottom, params.params[0] || 1));
|
||||
} else {
|
||||
this._moveCursor(0, params.params[0] || 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -679,7 +696,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Other than cursorDown (CUD) also set the cursor to first column.
|
||||
*/
|
||||
public cursorNextLine(params: IParams): void {
|
||||
this._moveCursor(0, params.params[0] || 1);
|
||||
this.cursorDown(params);
|
||||
this._bufferService.buffer.x = 0;
|
||||
}
|
||||
|
||||
@@ -689,7 +706,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Other than cursorUp (CUU) also set the cursor to first column.
|
||||
*/
|
||||
public cursorPrecedingLine(params: IParams): void {
|
||||
this._moveCursor(0, -(params.params[0] || 1));
|
||||
this.cursorUp(params);
|
||||
this._bufferService.buffer.x = 0;
|
||||
}
|
||||
|
||||
@@ -1031,7 +1048,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
while (param--) {
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1);
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
|
||||
}
|
||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||
}
|
||||
@@ -1633,7 +1650,6 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to extract and apply color params/subparams.
|
||||
* Returns advance for params index.
|
||||
@@ -1906,6 +1922,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public deviceStatusPrivate(params: IParams): void {
|
||||
// modern xterm doesnt seem to
|
||||
// respond to any of these except ?6, 6, and 5
|
||||
@@ -2134,10 +2151,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*/
|
||||
public index(): void {
|
||||
this._restrictCursor();
|
||||
const buffer = this._bufferService.buffer;
|
||||
this._bufferService.buffer.y++;
|
||||
if (this._bufferService.buffer.y > this._bufferService.buffer.scrollBottom) {
|
||||
this._bufferService.buffer.y--;
|
||||
if (buffer.y === buffer.scrollBottom + 1) {
|
||||
buffer.y--;
|
||||
this._terminal.scroll();
|
||||
} else if (buffer.y >= this._bufferService.rows) {
|
||||
buffer.y = this._bufferService.rows - 1;
|
||||
}
|
||||
this._restrictCursor();
|
||||
}
|
||||
|
||||
@@ -1123,6 +1123,241 @@ describe('Terminal', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Buffer.stringIndexToBufferIndex', () => {
|
||||
let terminal: TestTerminal;
|
||||
|
||||
beforeEach(() => {
|
||||
terminal = new TestTerminal({rows: 5, cols: 10, scrollback: 5});
|
||||
});
|
||||
|
||||
it('multiline ascii', () => {
|
||||
const input = 'This is ASCII text spanning multiple lines.';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('combining e\u0301 in a sentence', () => {
|
||||
const input = 'Sitting in the cafe\u0301 drinking coffee.';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < 19; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
}
|
||||
// string index 18 & 19 point to combining char e\u0301 ---> same buffer Index
|
||||
assert.deepEqual(
|
||||
terminal.buffer.stringIndexToBufferIndex(0, 18),
|
||||
terminal.buffer.stringIndexToBufferIndex(0, 19));
|
||||
// after the combining char every string index has an offset of -1
|
||||
for (let i = 19; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i - 1) / terminal.cols) | 0, (i - 1) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('multiline combining e\u0301', () => {
|
||||
const input = 'e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
// every buffer cell index contains 2 string indices
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i >> 1) / terminal.cols) | 0, (i >> 1) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('surrogate char in a sentence', () => {
|
||||
const input = 'The 𝄞 is a clef widely used in modern notation.';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
}
|
||||
// string index 4 & 5 point to surrogate char 𝄞 ---> same buffer Index
|
||||
assert.deepEqual(
|
||||
terminal.buffer.stringIndexToBufferIndex(0, 4),
|
||||
terminal.buffer.stringIndexToBufferIndex(0, 5));
|
||||
// after the combining char every string index has an offset of -1
|
||||
for (let i = 5; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i - 1) / terminal.cols) | 0, (i - 1) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('multiline surrogate char', () => {
|
||||
const input = '𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
// every buffer cell index contains 2 string indices
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i >> 1) / terminal.cols) | 0, (i >> 1) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('surrogate char with combining', () => {
|
||||
// eye of Ra with acute accent - string length of 3
|
||||
const input = '𓂀\u0301 - the eye hiroglyph with an acute accent.';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
// index 0..2 should map to 0
|
||||
assert.deepEqual([0, 0], terminal.buffer.stringIndexToBufferIndex(0, 1));
|
||||
assert.deepEqual([0, 0], terminal.buffer.stringIndexToBufferIndex(0, 2));
|
||||
for (let i = 2; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i - 2) / terminal.cols) | 0, (i - 2) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('multiline surrogate with combining', () => {
|
||||
const input = '𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
// every buffer cell index contains 3 string indices
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([(((i / 3) | 0) / terminal.cols) | 0, ((i / 3) | 0) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('fullwidth chars', () => {
|
||||
const input = 'These 123 are some fat numbers.';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < 6; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
}
|
||||
// string index 6, 7, 8 take 2 cells
|
||||
assert.deepEqual([0, 8], terminal.buffer.stringIndexToBufferIndex(0, 7));
|
||||
assert.deepEqual([1, 0], terminal.buffer.stringIndexToBufferIndex(0, 8));
|
||||
// rest of the string has offset of +3
|
||||
for (let i = 9; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i + 3) / terminal.cols) | 0, (i + 3) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('multiline fullwidth chars', () => {
|
||||
const input = '12345678901234567890';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 9; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i << 1) / terminal.cols) | 0, (i << 1) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('fullwidth combining with emoji - match emoji cell', () => {
|
||||
const input = 'Lots of ¥\u0301 make me 😃.';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
const stringIndex = s.match(/😃/).index;
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, stringIndex);
|
||||
assert(terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars(), '😃');
|
||||
});
|
||||
|
||||
it('multiline fullwidth chars with offset 1 (currently tests for broken behavior)', () => {
|
||||
const input = 'a12345678901234567890';
|
||||
// the 'a' at the beginning moves all fullwidth chars one to the right
|
||||
// now the end of the line contains a dangling empty cell since
|
||||
// the next fullwidth char has to wrap early
|
||||
// the dangling last cell is wrongly added in the string
|
||||
// --> fixable after resolving #1685
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 10; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||
const j = (i - 0) << 1;
|
||||
assert.deepEqual([(j / terminal.cols) | 0, j % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('test fully wrapped buffer up to last char', () => {
|
||||
const input = Array(6).join('1234567890');
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||
assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
|
||||
}
|
||||
});
|
||||
|
||||
it('test fully wrapped buffer up to last char with full width odd', () => {
|
||||
const input = 'a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301'
|
||||
+ 'a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||
assert.equal(
|
||||
(!(i % 3))
|
||||
? input[i]
|
||||
: (i % 3 === 1)
|
||||
? input.substr(i, 2)
|
||||
: input.substr(i - 1, 2),
|
||||
terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle \t in lines correctly', () => {
|
||||
const input = '\thttps://google.de';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(s, Array(terminal.optionsService.options.tabStopWidth + 1).join(' ') + 'https://google.de');
|
||||
});
|
||||
});
|
||||
|
||||
describe('BufferStringIterator', function(): void {
|
||||
it('iterator does not overflow buffer limits', function(): void {
|
||||
const terminal = new TestTerminal({rows: 5, cols: 10, scrollback: 5});
|
||||
const data = [
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaa\n',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaa\n',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaa\n',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa'
|
||||
];
|
||||
terminal.writeSync(data.join(''));
|
||||
// brute force test with insane values
|
||||
expect(() => {
|
||||
for (let overscan = 0; overscan < 20; ++overscan) {
|
||||
for (let start = -10; start < 20; ++start) {
|
||||
for (let end = -10; end < 20; ++end) {
|
||||
const it = terminal.buffer.iterator(false, start, end, overscan, overscan);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).to.not.throw();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class TestLinkifier extends Linkifier {
|
||||
|
||||
+5
-2
@@ -394,6 +394,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
||||
case 'rendererType':
|
||||
if (this._renderService) {
|
||||
this._renderService.setRenderer(this._createRenderer());
|
||||
this._renderService.onResize(this.cols, this.rows);
|
||||
}
|
||||
break;
|
||||
case 'scrollback':
|
||||
@@ -643,7 +644,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
||||
this.screenElement);
|
||||
this._instantiationService.setService(ISelectionService, this._selectionService);
|
||||
this.register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire()));
|
||||
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService.onMouseDown(e)));
|
||||
this.register(this._selectionService.onRedrawRequest(e => this._renderService.onSelectionChanged(e.start, e.end, e.columnSelectMode)));
|
||||
this.register(this._selectionService.onLinuxMouseSelection(text => {
|
||||
// If there's a new selection, put it into the textarea, focus and select it
|
||||
@@ -664,10 +664,13 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
||||
this.register(this.onScroll(() => this._mouseZoneManager.clearAll()));
|
||||
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
|
||||
|
||||
// This event listener must be registered aftre MouseZoneManager is created
|
||||
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService.onMouseDown(e)));
|
||||
|
||||
// apply mouse event classes set by escape codes before terminal was attached
|
||||
this.element.classList.toggle('enable-mouse-events', this.mouseEvents);
|
||||
if (this.mouseEvents) {
|
||||
this._selectionService.disable();
|
||||
this.element.classList.add('enable-mouse-events');
|
||||
} else {
|
||||
this._selectionService.enable();
|
||||
}
|
||||
|
||||
@@ -17,12 +17,6 @@ const ROWS = 25;
|
||||
|
||||
const TESTFILES = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '..')});
|
||||
const SKIP_FILES = [
|
||||
't0070-DECSTBM_LF.in', // lineFeed not working correctly
|
||||
't0071-DECSTBM_IND.in',
|
||||
't0072-DECSTBM_NEL.in',
|
||||
't0075-DECSTBM_CUU_CUD.in',
|
||||
't0076-DECSTBM_IL_DL.in', // not working due to lineFeed
|
||||
't0077-DECSTBM_quirks.in',
|
||||
't0084-CBT.in',
|
||||
't0101-NLM.in',
|
||||
't0103-reverse_wrap.in',
|
||||
@@ -42,7 +36,7 @@ const FILES = TESTFILES.filter(value => SKIP_FILES.indexOf(value.split('/').slic
|
||||
|
||||
|
||||
describe('Escape Sequence Files', function(): void {
|
||||
this.timeout(20000);
|
||||
this.timeout(1000);
|
||||
|
||||
let ptyTerm: any;
|
||||
let slaveEnd: any;
|
||||
|
||||
Vendored
+1
-1
@@ -116,7 +116,7 @@ export interface IInputHandler {
|
||||
/** CSI a */ hPositionRelative(params: IParams): void;
|
||||
/** CSI b */ repeatPrecedingCharacter(params: IParams): void;
|
||||
/** CSI c */ sendDeviceAttributesPrimary(params: IParams): void;
|
||||
sendDeviceAttributesSecondary(params: IParams): void;
|
||||
/** CSI > c */ sendDeviceAttributesSecondary(params: IParams): void;
|
||||
/** CSI d */ linePosAbsolute(params: IParams): void;
|
||||
/** CSI e */ vPositionRelative(params: IParams): void;
|
||||
/** CSI f */ hVPosition(params: IParams): void;
|
||||
|
||||
@@ -108,7 +108,7 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
}
|
||||
|
||||
public setRenderer(renderer: IRenderer): void {
|
||||
// TODO: RenderCoordinator should be the only one to dispose the renderer
|
||||
// TODO: RenderService should be the only one to dispose the renderer
|
||||
this._renderer.dispose();
|
||||
this._renderer = renderer;
|
||||
this.refreshRows(0, this._rowCount - 1);
|
||||
|
||||
@@ -563,9 +563,10 @@ export class SelectionService implements ISelectionService {
|
||||
// to be sent to the pty.
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
// Something went wrong
|
||||
// Do nothing if there is no selection start, this can happen if the first
|
||||
// click in the terminal is an incremental click
|
||||
if (!this._model.selectionStart) {
|
||||
throw new Error('Selection start position was not set before mousemove event');
|
||||
return;
|
||||
}
|
||||
|
||||
// Record the previous position so we know whether to redraw the selection
|
||||
|
||||
@@ -1163,237 +1163,4 @@ describe('Buffer', () => {
|
||||
assert.equal(str3, '😁a');
|
||||
});
|
||||
});
|
||||
// describe('stringIndexToBufferIndex', () => {
|
||||
// let terminal: TestTerminal;
|
||||
|
||||
// beforeEach(() => {
|
||||
// terminal = new TestTerminal({rows: 5, cols: 10, scrollback: 5});
|
||||
// });
|
||||
|
||||
// it('multiline ascii', () => {
|
||||
// const input = 'This is ASCII text spanning multiple lines.';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 0; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('combining e\u0301 in a sentence', () => {
|
||||
// const input = 'Sitting in the cafe\u0301 drinking coffee.';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 0; i < 19; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// // string index 18 & 19 point to combining char e\u0301 ---> same buffer Index
|
||||
// assert.deepEqual(
|
||||
// terminal.buffer.stringIndexToBufferIndex(0, 18),
|
||||
// terminal.buffer.stringIndexToBufferIndex(0, 19));
|
||||
// // after the combining char every string index has an offset of -1
|
||||
// for (let i = 19; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i - 1) / terminal.cols) | 0, (i - 1) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('multiline combining e\u0301', () => {
|
||||
// const input = 'e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// // every buffer cell index contains 2 string indices
|
||||
// for (let i = 0; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i >> 1) / terminal.cols) | 0, (i >> 1) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('surrogate char in a sentence', () => {
|
||||
// const input = 'The 𝄞 is a clef widely used in modern notation.';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 0; i < 5; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// // string index 4 & 5 point to surrogate char 𝄞 ---> same buffer Index
|
||||
// assert.deepEqual(
|
||||
// terminal.buffer.stringIndexToBufferIndex(0, 4),
|
||||
// terminal.buffer.stringIndexToBufferIndex(0, 5));
|
||||
// // after the combining char every string index has an offset of -1
|
||||
// for (let i = 5; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i - 1) / terminal.cols) | 0, (i - 1) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('multiline surrogate char', () => {
|
||||
// const input = '𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// // every buffer cell index contains 2 string indices
|
||||
// for (let i = 0; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i >> 1) / terminal.cols) | 0, (i >> 1) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('surrogate char with combining', () => {
|
||||
// // eye of Ra with acute accent - string length of 3
|
||||
// const input = '𓂀\u0301 - the eye hiroglyph with an acute accent.';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// // index 0..2 should map to 0
|
||||
// assert.deepEqual([0, 0], terminal.buffer.stringIndexToBufferIndex(0, 1));
|
||||
// assert.deepEqual([0, 0], terminal.buffer.stringIndexToBufferIndex(0, 2));
|
||||
// for (let i = 2; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i - 2) / terminal.cols) | 0, (i - 2) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('multiline surrogate with combining', () => {
|
||||
// const input = '𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// // every buffer cell index contains 3 string indices
|
||||
// for (let i = 0; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([(((i / 3) | 0) / terminal.cols) | 0, ((i / 3) | 0) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('fullwidth chars', () => {
|
||||
// const input = 'These 123 are some fat numbers.';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 0; i < 6; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// // string index 6, 7, 8 take 2 cells
|
||||
// assert.deepEqual([0, 8], terminal.buffer.stringIndexToBufferIndex(0, 7));
|
||||
// assert.deepEqual([1, 0], terminal.buffer.stringIndexToBufferIndex(0, 8));
|
||||
// // rest of the string has offset of +3
|
||||
// for (let i = 9; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i + 3) / terminal.cols) | 0, (i + 3) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('multiline fullwidth chars', () => {
|
||||
// const input = '12345678901234567890';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 9; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i << 1) / terminal.cols) | 0, (i << 1) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('fullwidth combining with emoji - match emoji cell', () => {
|
||||
// const input = 'Lots of ¥\u0301 make me 😃.';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// const stringIndex = s.match(/😃/).index;
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, stringIndex);
|
||||
// assert(terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars(), '😃');
|
||||
// });
|
||||
|
||||
// it('multiline fullwidth chars with offset 1 (currently tests for broken behavior)', () => {
|
||||
// const input = 'a12345678901234567890';
|
||||
// // the 'a' at the beginning moves all fullwidth chars one to the right
|
||||
// // now the end of the line contains a dangling empty cell since
|
||||
// // the next fullwidth char has to wrap early
|
||||
// // the dangling last cell is wrongly added in the string
|
||||
// // --> fixable after resolving #1685
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 10; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||
// const j = (i - 0) << 1;
|
||||
// assert.deepEqual([(j / terminal.cols) | 0, j % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('test fully wrapped buffer up to last char', () => {
|
||||
// const input = Array(6).join('1234567890');
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 0; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||
// assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('test fully wrapped buffer up to last char with full width odd', () => {
|
||||
// const input = 'a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301'
|
||||
// + 'a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 0; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||
// assert.equal(
|
||||
// (!(i % 3))
|
||||
// ? input[i]
|
||||
// : (i % 3 === 1)
|
||||
// ? input.substr(i, 2)
|
||||
// : input.substr(i - 1, 2),
|
||||
// terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('should handle \t in lines correctly', () => {
|
||||
// const input = '\thttps://google.de';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(s, Array(optionsService.options.tabStopWidth + 1).join(' ') + 'https://google.de');
|
||||
// });
|
||||
// });
|
||||
// describe('BufferStringIterator', function(): void {
|
||||
// it('iterator does not overflow buffer limits', function(): void {
|
||||
// const terminal = new TestTerminal({rows: 5, cols: 10, scrollback: 5});
|
||||
// const data = [
|
||||
// 'aaaaaaaaaa',
|
||||
// 'aaaaaaaaa\n',
|
||||
// 'aaaaaaaaaa',
|
||||
// 'aaaaaaaaa\n',
|
||||
// 'aaaaaaaaaa',
|
||||
// 'aaaaaaaaaa',
|
||||
// 'aaaaaaaaaa',
|
||||
// 'aaaaaaaaa\n',
|
||||
// 'aaaaaaaaaa',
|
||||
// 'aaaaaaaaaa'
|
||||
// ];
|
||||
// terminal.writeSync(data.join(''));
|
||||
// // brute force test with insane values
|
||||
// expect(() => {
|
||||
// for (let overscan = 0; overscan < 20; ++overscan) {
|
||||
// for (let start = -10; start < 20; ++start) {
|
||||
// for (let end = -10; end < 20; ++end) {
|
||||
// const it = terminal.buffer.iterator(false, start, end, overscan, overscan);
|
||||
// while (it.hasNext()) {
|
||||
// it.next();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }).to.not.throw();
|
||||
// });
|
||||
// });
|
||||
});
|
||||
|
||||
Vendored
+1
-1
@@ -40,7 +40,7 @@ export interface IBuffer {
|
||||
nextStop(x?: number): number;
|
||||
prevStop(x?: number): number;
|
||||
getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine;
|
||||
stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[];
|
||||
stringIndexToBufferIndex(lineIndex: number, stringIndex: number, trimRight?: boolean): number[];
|
||||
iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator;
|
||||
getNullCell(attr?: IAttributeData): ICellData;
|
||||
getWhitespaceCell(attr?: IAttributeData): ICellData;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
import { assert } from 'chai';
|
||||
import { DcsParser, DcsHandlerFactory } from 'common/parser/DcsParser';
|
||||
import { DcsParser, DcsHandler } from 'common/parser/DcsParser';
|
||||
import { IDcsHandler, IParams, IFunctionIdentifier } from 'common/parser/Types';
|
||||
import { utf32ToString, StringToUtf32 } from 'common/input/TextDecoder';
|
||||
import { Params } from 'common/parser/Params';
|
||||
@@ -75,7 +75,7 @@ describe('DcsParser', () => {
|
||||
beforeEach(() => {
|
||||
reports = [];
|
||||
parser = new DcsParser();
|
||||
parser.setDcsHandlerFallback((id, action, data) => {
|
||||
parser.setHandlerFallback((id, action, data) => {
|
||||
if (action === 'HOOK') {
|
||||
data = data.toArray();
|
||||
}
|
||||
@@ -84,7 +84,7 @@ describe('DcsParser', () => {
|
||||
});
|
||||
describe('handler registration', () => {
|
||||
it('setDcsHandler', () => {
|
||||
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th'));
|
||||
parser.setHandler(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,8 +100,8 @@ describe('DcsParser', () => {
|
||||
]);
|
||||
});
|
||||
it('clearDcsHandler', () => {
|
||||
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th'));
|
||||
parser.clearDcsHandler(identifier({intermediates: '+', final: 'p'}));
|
||||
parser.setHandler(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');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -117,8 +117,8 @@ describe('DcsParser', () => {
|
||||
]);
|
||||
});
|
||||
it('addDcsHandler', () => {
|
||||
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
|
||||
parser.addDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2'));
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
|
||||
parser.addHandler(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 +137,8 @@ describe('DcsParser', () => {
|
||||
]);
|
||||
});
|
||||
it('addDcsHandler with return false', () => {
|
||||
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
|
||||
parser.addDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2', true));
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
|
||||
parser.addHandler(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 +157,8 @@ describe('DcsParser', () => {
|
||||
]);
|
||||
});
|
||||
it('dispose handlers', () => {
|
||||
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
|
||||
const dispo = parser.addDcsHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th2', true));
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new TestHandler(reports, 'th1'));
|
||||
const dispo = parser.addHandler(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 +176,7 @@ describe('DcsParser', () => {
|
||||
});
|
||||
describe('DcsHandlerFactory', () => {
|
||||
it('should be called once on end(true)', () => {
|
||||
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push([params.toArray(), data])));
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push([params.toArray(), data])));
|
||||
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -186,7 +186,7 @@ describe('DcsParser', () => {
|
||||
assert.deepEqual(reports, [[[1, 2, 3], 'Here comes the mouse!']]);
|
||||
});
|
||||
it('should not be called on end(false)', () => {
|
||||
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push([params.toArray(), data])));
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push([params.toArray(), data])));
|
||||
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -196,8 +196,8 @@ describe('DcsParser', () => {
|
||||
assert.deepEqual(reports, []);
|
||||
});
|
||||
it('should be disposable', () => {
|
||||
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push(['one', params.toArray(), data])));
|
||||
const dispo = parser.addDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push(['two', params.toArray(), data])));
|
||||
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.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -215,8 +215,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.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push(['one', params.toArray(), data])));
|
||||
parser.addDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => { reports.push(['two', params.toArray(), data]); 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.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
|
||||
let data = toUtf32('Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -227,7 +227,7 @@ describe('DcsParser', () => {
|
||||
});
|
||||
it('should work up to payload limit', function(): void {
|
||||
this.timeout(10000);
|
||||
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push([params.toArray(), data])));
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push([params.toArray(), data])));
|
||||
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 +238,7 @@ describe('DcsParser', () => {
|
||||
});
|
||||
it('should abort for payload limit +1', function(): void {
|
||||
this.timeout(10000);
|
||||
parser.setDcsHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandlerFactory((data, params) => reports.push([params.toArray(), data])));
|
||||
parser.setHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => reports.push([params.toArray(), data])));
|
||||
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) {
|
||||
|
||||
@@ -4,25 +4,25 @@
|
||||
*/
|
||||
|
||||
import { IDisposable } from 'common/Types';
|
||||
import { IDcsHandler, IParams, IHandlerCollection, IDcsParser, DcsFallbackHandler } from 'common/parser/Types';
|
||||
import { IDcsHandler, IParams, IHandlerCollection, IDcsParser, DcsFallbackHandlerType } from 'common/parser/Types';
|
||||
import { utf32ToString } from 'common/input/TextDecoder';
|
||||
import { Params } from 'common/parser/Params';
|
||||
import { PAYLOAD_LIMIT } from 'common/parser/Constants';
|
||||
|
||||
const EMPTY_HANDLERS: IDcsHandler[] = [];
|
||||
|
||||
export class DcsParser implements IDcsParser {
|
||||
private _handlers: IHandlerCollection<IDcsHandler> = Object.create(null);
|
||||
private _empty: IDcsHandler[] = [];
|
||||
private _active: IDcsHandler[] = this._empty;
|
||||
private _active: IDcsHandler[] = EMPTY_HANDLERS;
|
||||
private _ident: number = 0;
|
||||
private _handlerFb: DcsFallbackHandler = () => {};
|
||||
private _handlerFb: DcsFallbackHandlerType = () => {};
|
||||
|
||||
public dispose(): void {
|
||||
this._handlers = Object.create(null);
|
||||
this._handlerFb = () => {};
|
||||
}
|
||||
|
||||
public addDcsHandler(ident: number, handler: IDcsHandler): IDisposable {
|
||||
public addHandler(ident: number, handler: IDcsHandler): IDisposable {
|
||||
if (this._handlers[ident] === undefined) {
|
||||
this._handlers[ident] = [];
|
||||
}
|
||||
@@ -38,15 +38,15 @@ export class DcsParser implements IDcsParser {
|
||||
};
|
||||
}
|
||||
|
||||
public setDcsHandler(ident: number, handler: IDcsHandler): void {
|
||||
public setHandler(ident: number, handler: IDcsHandler): void {
|
||||
this._handlers[ident] = [handler];
|
||||
}
|
||||
|
||||
public clearDcsHandler(ident: number): void {
|
||||
public clearHandler(ident: number): void {
|
||||
if (this._handlers[ident]) delete this._handlers[ident];
|
||||
}
|
||||
|
||||
public setDcsHandlerFallback(handler: DcsFallbackHandler): void {
|
||||
public setHandlerFallback(handler: DcsFallbackHandlerType): void {
|
||||
this._handlerFb = handler;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export class DcsParser implements IDcsParser {
|
||||
if (this._active.length) {
|
||||
this.unhook(false);
|
||||
}
|
||||
this._active = this._empty;
|
||||
this._active = EMPTY_HANDLERS;
|
||||
this._ident = 0;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export class DcsParser implements IDcsParser {
|
||||
// always reset leftover handlers
|
||||
this.reset();
|
||||
this._ident = ident;
|
||||
this._active = this._handlers[ident] || this._empty;
|
||||
this._active = this._handlers[ident] || EMPTY_HANDLERS;
|
||||
if (!this._active.length) {
|
||||
this._handlerFb(this._ident, 'HOOK', params);
|
||||
} else {
|
||||
@@ -98,7 +98,7 @@ export class DcsParser implements IDcsParser {
|
||||
this._active[j].unhook(false);
|
||||
}
|
||||
}
|
||||
this._active = this._empty;
|
||||
this._active = EMPTY_HANDLERS;
|
||||
this._ident = 0;
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,7 @@ export class DcsParser implements IDcsParser {
|
||||
* Convenient class to create a DCS handler from a single callback function.
|
||||
* Note: The payload is currently limited to 50 MB (hardcoded).
|
||||
*/
|
||||
export class DcsHandlerFactory implements IDcsHandler {
|
||||
export class DcsHandler implements IDcsHandler {
|
||||
private _data = '';
|
||||
private _params: IParams | undefined;
|
||||
private _hitLimit: boolean = false;
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IParsingState, IParams, ParamsArray, IOscParser, IOscHandler, OscFallbackHandler } from 'common/parser/Types';
|
||||
import { IParsingState, IParams, ParamsArray, IOscParser, IOscHandler, OscFallbackHandlerType } 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';
|
||||
import { ParserState } from 'common/parser/Constants';
|
||||
import { Params } from 'common/parser/Params';
|
||||
import { OscHandlerFactory } from 'common/parser/OscParser';
|
||||
import { OscHandler } from 'common/parser/OscParser';
|
||||
import { IDisposable } from 'common/Types';
|
||||
import { DcsHandlerFactory } from 'common/parser/DcsParser';
|
||||
import { DcsHandler } from 'common/parser/DcsParser';
|
||||
|
||||
|
||||
function r(a: number, b: number): string[] {
|
||||
@@ -24,7 +24,7 @@ function r(a: number, b: number): string[] {
|
||||
}
|
||||
|
||||
class MockOscPutParser implements IOscParser {
|
||||
private _fallback: OscFallbackHandler = () => {};
|
||||
private _fallback: OscFallbackHandlerType = () => {};
|
||||
public data = '';
|
||||
public reset(): void {
|
||||
this.data = '';
|
||||
@@ -41,16 +41,16 @@ class MockOscPutParser implements IOscParser {
|
||||
this._fallback(id, 'END', this.data.slice(this.data.indexOf(';') + 1));
|
||||
}
|
||||
}
|
||||
addOscHandler(ident: number, handler: IOscHandler): IDisposable {
|
||||
addHandler(ident: number, handler: IOscHandler): IDisposable {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
setOscHandler(ident: number, handler: IOscHandler): void {
|
||||
setHandler(ident: number, handler: IOscHandler): void {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
clearOscHandler(ident: number): void {
|
||||
clearHandler(ident: number): void {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
setOscHandlerFallback(handler: OscFallbackHandler): void {
|
||||
setHandlerFallback(handler: OscFallbackHandlerType): void {
|
||||
this._fallback = handler;
|
||||
}
|
||||
}
|
||||
@@ -1220,26 +1220,26 @@ describe('EscapeSequenceParser', function (): void {
|
||||
});
|
||||
describe('ESC custom handlers', () => {
|
||||
it('prevent fallback', () => {
|
||||
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => esc.push('default - %G'));
|
||||
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
|
||||
parser2.addEscHandler({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.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
|
||||
parser2.addEscHandler({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.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; });
|
||||
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.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; });
|
||||
parse(parser2, INPUT);
|
||||
@@ -1247,21 +1247,21 @@ describe('EscapeSequenceParser', function (): void {
|
||||
});
|
||||
it('Execution order should go from latest handler down to the original', () => {
|
||||
const order: number[] = [];
|
||||
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => order.push(1));
|
||||
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; });
|
||||
parse(parser2, '\x1b%G');
|
||||
chai.expect(order).eql([3, 2, 1]);
|
||||
});
|
||||
it('Dispose should work', () => {
|
||||
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => esc.push('default - %G'));
|
||||
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
|
||||
const dispo = parser2.addEscHandler({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'));
|
||||
parser2.setEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); });
|
||||
const dispo = parser2.addEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; });
|
||||
dispo.dispose();
|
||||
dispo.dispose();
|
||||
@@ -1284,7 +1284,7 @@ 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.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
|
||||
parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
|
||||
parse(parser2, INPUT);
|
||||
chai.expect(csi).eql([], 'Should not fallback to original handler');
|
||||
@@ -1292,7 +1292,7 @@ describe('EscapeSequenceParser', function (): void {
|
||||
});
|
||||
it('Allow fallback', () => {
|
||||
const csiCustom: [string, ParamsArray, string][] = [];
|
||||
parser2.setCsiHandler({final: 'm'}, params => csi.push(['m', params.toArray(), '']));
|
||||
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
|
||||
parser2.addCsiHandler({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');
|
||||
@@ -1301,7 +1301,7 @@ 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.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; });
|
||||
parse(parser2, INPUT);
|
||||
@@ -1312,7 +1312,7 @@ 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.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; });
|
||||
parse(parser2, INPUT);
|
||||
@@ -1322,7 +1322,7 @@ 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.setCsiHandler({final: 'm'}, () => { order.push(1); });
|
||||
parser2.addCsiHandler({final: 'm'}, () => { order.push(2); return false; });
|
||||
parser2.addCsiHandler({final: 'm'}, () => { order.push(3); return false; });
|
||||
parse(parser2, '\x1b[0m');
|
||||
@@ -1330,7 +1330,7 @@ describe('EscapeSequenceParser', function (): void {
|
||||
});
|
||||
it('Dispose should work', () => {
|
||||
const csiCustom: [string, ParamsArray, string][] = [];
|
||||
parser2.setCsiHandler({final: 'm'}, params => csi.push(['m', params.toArray(), '']));
|
||||
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
|
||||
const customHandler = parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
|
||||
customHandler.dispose();
|
||||
parse(parser2, INPUT);
|
||||
@@ -1339,7 +1339,7 @@ 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(), '']));
|
||||
parser2.setCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); });
|
||||
const customHandler = parser2.addCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
|
||||
customHandler.dispose();
|
||||
customHandler.dispose();
|
||||
@@ -1364,7 +1364,7 @@ describe('EscapeSequenceParser', function (): void {
|
||||
chai.expect(exe).eql(['\n']);
|
||||
});
|
||||
it('OSC handler', function (): void {
|
||||
parser2.setOscHandler(1, new OscHandlerFactory(function (data: string): void {
|
||||
parser2.setOscHandler(1, new OscHandler(function (data: string): void {
|
||||
osc.push([1, data]);
|
||||
}));
|
||||
parse(parser2, INPUT);
|
||||
@@ -1378,16 +1378,16 @@ describe('EscapeSequenceParser', function (): void {
|
||||
describe('OSC custom handlers', () => {
|
||||
it('Prevent fallback', () => {
|
||||
const oscCustom: [number, string][] = [];
|
||||
parser2.setOscHandler(1, new OscHandlerFactory(data => osc.push([1, data])));
|
||||
parser2.addOscHandler(1, new OscHandlerFactory(data => { oscCustom.push([1, data]); return true; }));
|
||||
parser2.setOscHandler(1, new OscHandler(data => osc.push([1, data])));
|
||||
parser2.addOscHandler(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 OscHandlerFactory(data => osc.push([1, data])));
|
||||
parser2.addOscHandler(1, new OscHandlerFactory(data => { oscCustom.push([1, data]); return false; }));
|
||||
parser2.setOscHandler(1, new OscHandler(data => osc.push([1, data])));
|
||||
parser2.addOscHandler(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']]);
|
||||
@@ -1395,9 +1395,9 @@ describe('EscapeSequenceParser', function (): void {
|
||||
it('Multiple custom handlers fallback once', () => {
|
||||
const oscCustom: [number, string][] = [];
|
||||
const oscCustom2: [number, string][] = [];
|
||||
parser2.setOscHandler(1, new OscHandlerFactory(data => osc.push([1, data])));
|
||||
parser2.addOscHandler(1, new OscHandlerFactory(data => { oscCustom.push([1, data]); return true; }));
|
||||
parser2.addOscHandler(1, new OscHandlerFactory(data => { oscCustom2.push([1, data]); return false; }));
|
||||
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; }));
|
||||
parse(parser2, INPUT);
|
||||
chai.expect(osc).eql([], 'Should not fallback to original handler');
|
||||
chai.expect(oscCustom).eql([[1, 'foo=bar']]);
|
||||
@@ -1406,9 +1406,9 @@ describe('EscapeSequenceParser', function (): void {
|
||||
it('Multiple custom handlers no fallback', () => {
|
||||
const oscCustom: [number, string][] = [];
|
||||
const oscCustom2: [number, string][] = [];
|
||||
parser2.setOscHandler(1, new OscHandlerFactory(data => osc.push([1, data])));
|
||||
parser2.addOscHandler(1, new OscHandlerFactory(data => { oscCustom.push([1, data]); return true; }));
|
||||
parser2.addOscHandler(1, new OscHandlerFactory(data => { oscCustom2.push([1, data]); return true; }));
|
||||
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; }));
|
||||
parse(parser2, INPUT);
|
||||
chai.expect(osc).eql([], 'Should not fallback to original handler');
|
||||
chai.expect(oscCustom).eql([], 'Should not fallback once');
|
||||
@@ -1416,16 +1416,16 @@ describe('EscapeSequenceParser', function (): void {
|
||||
});
|
||||
it('Execution order should go from latest handler down to the original', () => {
|
||||
const order: number[] = [];
|
||||
parser2.setOscHandler(1, new OscHandlerFactory(() => order.push(1)));
|
||||
parser2.addOscHandler(1, new OscHandlerFactory(() => { order.push(2); return false; }));
|
||||
parser2.addOscHandler(1, new OscHandlerFactory(() => { order.push(3); return false; }));
|
||||
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; }));
|
||||
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 OscHandlerFactory(data => osc.push([1, data])));
|
||||
const customHandler = parser2.addOscHandler(1, new OscHandlerFactory(data => { oscCustom.push([1, data]); return true; }));
|
||||
parser2.setOscHandler(1, new OscHandler(data => osc.push([1, data])));
|
||||
const customHandler = parser2.addOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
|
||||
customHandler.dispose();
|
||||
parse(parser2, INPUT);
|
||||
chai.expect(osc).eql([[1, 'foo=bar']]);
|
||||
@@ -1433,8 +1433,8 @@ describe('EscapeSequenceParser', function (): void {
|
||||
});
|
||||
it('Should not corrupt the parser when dispose is called twice', () => {
|
||||
const oscCustom: [number, string][] = [];
|
||||
parser2.setOscHandler(1, new OscHandlerFactory(data => osc.push([1, data])));
|
||||
const customHandler = parser2.addOscHandler(1, new OscHandlerFactory(data => { oscCustom.push([1, data]); return true; }));
|
||||
parser2.setOscHandler(1, new OscHandler(data => osc.push([1, data])));
|
||||
const customHandler = parser2.addOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
|
||||
customHandler.dispose();
|
||||
customHandler.dispose();
|
||||
parse(parser2, INPUT);
|
||||
@@ -1476,54 +1476,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 DcsHandlerFactory((data, params) => dcsCustom.push(['A', params.toArray(), data])));
|
||||
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandlerFactory((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
|
||||
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; }));
|
||||
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 DcsHandlerFactory((data, params) => dcsCustom.push(['A', params.toArray(), data])));
|
||||
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandlerFactory((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return false; }));
|
||||
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; }));
|
||||
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 DcsHandlerFactory((data, params) => dcsCustom.push(['A', params.toArray(), data])));
|
||||
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandlerFactory((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
|
||||
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandlerFactory((data, params) => { dcsCustom.push(['C', params.toArray(), data]); return false; }));
|
||||
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; }));
|
||||
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 DcsHandlerFactory((data, params) => dcsCustom.push(['A', params.toArray(), data])));
|
||||
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandlerFactory((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
|
||||
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandlerFactory((data, params) => { dcsCustom.push(['C', params.toArray(), data]); return true; }));
|
||||
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; }));
|
||||
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 DcsHandlerFactory(() => order.push(1)));
|
||||
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandlerFactory(() => { order.push(2); return false; }));
|
||||
parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandlerFactory(() => { order.push(3); return false; }));
|
||||
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; }));
|
||||
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 DcsHandlerFactory((data, params) => dcsCustom.push(['A', params.toArray(), data])));
|
||||
const dispo = parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandlerFactory((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
|
||||
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; }));
|
||||
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 DcsHandlerFactory((data, params) => dcsCustom.push(['A', params.toArray(), data])));
|
||||
const dispo = parser2.addDcsHandler({intermediates: '+', final: 'p'}, new DcsHandlerFactory((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
|
||||
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; }));
|
||||
dispo.dispose();
|
||||
dispo.dispose();
|
||||
parse(parser2, DCS_INPUT);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IOscHandler, IHandlerCollection, CsiHandler, OscFallbackHandler, IOscParser, EscHandler, IDcsParser, DcsFallbackHandler, IFunctionIdentifier } from 'common/parser/Types';
|
||||
import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IOscHandler, IHandlerCollection, CsiHandlerType, OscFallbackHandlerType, IOscParser, EscHandlerType, IDcsParser, DcsFallbackHandlerType, IFunctionIdentifier, ExecuteFallbackHandlerType, CsiFallbackHandlerType, EscFallbackHandlerType, PrintHandlerType, PrintFallbackHandlerType, ExecuteHandlerType } from 'common/parser/Types';
|
||||
import { ParserState, ParserAction } from 'common/parser/Constants';
|
||||
import { Disposable } from 'common/Lifecycle';
|
||||
import { IDisposable } from 'common/Types';
|
||||
@@ -238,19 +238,19 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
protected _collect: number;
|
||||
|
||||
// handler lookup containers
|
||||
protected _printHandler: (data: Uint32Array, start: number, end: number) => void;
|
||||
protected _executeHandlers: any;
|
||||
protected _csiHandlers: IHandlerCollection<CsiHandler>;
|
||||
protected _escHandlers: IHandlerCollection<EscHandler>;
|
||||
protected _printHandler: PrintHandlerType;
|
||||
protected _executeHandlers: {[flag: number]: ExecuteHandlerType};
|
||||
protected _csiHandlers: IHandlerCollection<CsiHandlerType>;
|
||||
protected _escHandlers: IHandlerCollection<EscHandlerType>;
|
||||
protected _oscParser: IOscParser;
|
||||
protected _dcsParser: IDcsParser;
|
||||
protected _errorHandler: (state: IParsingState) => IParsingState;
|
||||
|
||||
// fallback handlers
|
||||
protected _printHandlerFb: (data: Uint32Array, start: number, end: number) => void;
|
||||
protected _executeHandlerFb: (code: number) => void;
|
||||
protected _csiHandlerFb: (ident: number, params: IParams) => void;
|
||||
protected _escHandlerFb: (ident: number) => void;
|
||||
protected _printHandlerFb: PrintFallbackHandlerType;
|
||||
protected _executeHandlerFb: ExecuteFallbackHandlerType;
|
||||
protected _csiHandlerFb: CsiFallbackHandlerType;
|
||||
protected _escHandlerFb: EscFallbackHandlerType;
|
||||
protected _errorHandlerFb: (state: IParsingState) => IParsingState;
|
||||
|
||||
constructor(readonly TRANSITIONS: TransitionTable = VT500_TRANSITION_TABLE) {
|
||||
@@ -328,47 +328,74 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._executeHandlers = null;
|
||||
this._csiHandlers = Object.create(null);
|
||||
this._executeHandlers = Object.create(null);
|
||||
this._escHandlers = Object.create(null);
|
||||
this._oscParser.dispose();
|
||||
this._dcsParser.dispose();
|
||||
}
|
||||
|
||||
public setPrintHandler(callback: (data: Uint32Array, start: number, end: number) => void): void {
|
||||
this._printHandler = callback;
|
||||
public setPrintHandler(handler: PrintHandlerType): void {
|
||||
this._printHandler = handler;
|
||||
}
|
||||
public clearPrintHandler(): void {
|
||||
this._printHandler = this._printHandlerFb;
|
||||
}
|
||||
|
||||
public setExecuteHandler(flag: string, callback: () => void): void {
|
||||
this._executeHandlers[flag.charCodeAt(0)] = callback;
|
||||
}
|
||||
public clearExecuteHandler(flag: string): void {
|
||||
if (this._executeHandlers[flag.charCodeAt(0)]) delete this._executeHandlers[flag.charCodeAt(0)];
|
||||
}
|
||||
public setExecuteHandlerFallback(callback: (code: number) => void): void {
|
||||
this._executeHandlerFb = callback;
|
||||
}
|
||||
|
||||
public addCsiHandler(id: IFunctionIdentifier, callback: CsiHandler): IDisposable {
|
||||
const ident = this._identifier(id);
|
||||
if (this._csiHandlers[ident] === undefined) {
|
||||
this._csiHandlers[ident] = [];
|
||||
public addEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): IDisposable {
|
||||
const ident = this._identifier(id, [0x30, 0x7e]);
|
||||
if (this._escHandlers[ident] === undefined) {
|
||||
this._escHandlers[ident] = [];
|
||||
}
|
||||
const handlerList = this._csiHandlers[ident];
|
||||
handlerList.push(callback);
|
||||
const handlerList = this._escHandlers[ident];
|
||||
handlerList.push(handler);
|
||||
return {
|
||||
dispose: () => {
|
||||
const handlerIndex = handlerList.indexOf(callback);
|
||||
const handlerIndex = handlerList.indexOf(handler);
|
||||
if (handlerIndex !== -1) {
|
||||
handlerList.splice(handlerIndex, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
public setCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => void): void {
|
||||
this._csiHandlers[this._identifier(id)] = [callback];
|
||||
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])];
|
||||
}
|
||||
public setEscHandlerFallback(handler: EscFallbackHandlerType): void {
|
||||
this._escHandlerFb = handler;
|
||||
}
|
||||
|
||||
public setExecuteHandler(flag: string, handler: ExecuteHandlerType): void {
|
||||
this._executeHandlers[flag.charCodeAt(0)] = handler;
|
||||
}
|
||||
public clearExecuteHandler(flag: string): void {
|
||||
if (this._executeHandlers[flag.charCodeAt(0)]) delete this._executeHandlers[flag.charCodeAt(0)];
|
||||
}
|
||||
public setExecuteHandlerFallback(handler: ExecuteFallbackHandlerType): void {
|
||||
this._executeHandlerFb = handler;
|
||||
}
|
||||
|
||||
public addCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): IDisposable {
|
||||
const ident = this._identifier(id);
|
||||
if (this._csiHandlers[ident] === undefined) {
|
||||
this._csiHandlers[ident] = [];
|
||||
}
|
||||
const handlerList = this._csiHandlers[ident];
|
||||
handlerList.push(handler);
|
||||
return {
|
||||
dispose: () => {
|
||||
const handlerIndex = handlerList.indexOf(handler);
|
||||
if (handlerIndex !== -1) {
|
||||
handlerList.splice(handlerIndex, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
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)];
|
||||
@@ -377,56 +404,30 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._csiHandlerFb = callback;
|
||||
}
|
||||
|
||||
public addEscHandler(id: IFunctionIdentifier, callback: EscHandler): IDisposable {
|
||||
const ident = this._identifier(id, [0x30, 0x7e]);
|
||||
if (this._escHandlers[ident] === undefined) {
|
||||
this._escHandlers[ident] = [];
|
||||
}
|
||||
const handlerList = this._escHandlers[ident];
|
||||
handlerList.push(callback);
|
||||
return {
|
||||
dispose: () => {
|
||||
const handlerIndex = handlerList.indexOf(callback);
|
||||
if (handlerIndex !== -1) {
|
||||
handlerList.splice(handlerIndex, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
public addDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): IDisposable {
|
||||
return this._dcsParser.addHandler(this._identifier(id), handler);
|
||||
}
|
||||
public setEscHandler(id: IFunctionIdentifier, callback: () => void): void {
|
||||
this._escHandlers[this._identifier(id, [0x30, 0x7e])] = [callback];
|
||||
public setDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): void {
|
||||
this._dcsParser.setHandler(this._identifier(id), handler);
|
||||
}
|
||||
public clearEscHandler(id: IFunctionIdentifier): void {
|
||||
if (this._escHandlers[this._identifier(id, [0x30, 0x7e])]) delete this._escHandlers[this._identifier(id, [0x30, 0x7e])];
|
||||
public clearDcsHandler(id: IFunctionIdentifier): void {
|
||||
this._dcsParser.clearHandler(this._identifier(id));
|
||||
}
|
||||
public setEscHandlerFallback(callback: (ident: number) => void): void {
|
||||
this._escHandlerFb = callback;
|
||||
public setDcsHandlerFallback(handler: DcsFallbackHandlerType): void {
|
||||
this._dcsParser.setHandlerFallback(handler);
|
||||
}
|
||||
|
||||
public addOscHandler(ident: number, handler: IOscHandler): IDisposable {
|
||||
return this._oscParser.addOscHandler(ident, handler);
|
||||
return this._oscParser.addHandler(ident, handler);
|
||||
}
|
||||
public setOscHandler(ident: number, handler: IOscHandler): void {
|
||||
this._oscParser.setOscHandler(ident, handler);
|
||||
this._oscParser.setHandler(ident, handler);
|
||||
}
|
||||
public clearOscHandler(ident: number): void {
|
||||
this._oscParser.clearOscHandler(ident);
|
||||
this._oscParser.clearHandler(ident);
|
||||
}
|
||||
public setOscHandlerFallback(handler: OscFallbackHandler): void {
|
||||
this._oscParser.setOscHandlerFallback(handler);
|
||||
}
|
||||
|
||||
public addDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): IDisposable {
|
||||
return this._dcsParser.addDcsHandler(this._identifier(id), handler);
|
||||
}
|
||||
public setDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): void {
|
||||
this._dcsParser.setDcsHandler(this._identifier(id), handler);
|
||||
}
|
||||
public clearDcsHandler(id: IFunctionIdentifier): void {
|
||||
this._dcsParser.clearDcsHandler(this._identifier(id));
|
||||
}
|
||||
public setDcsHandlerFallback(handler: DcsFallbackHandler): void {
|
||||
this._dcsParser.setDcsHandlerFallback(handler);
|
||||
public setOscHandlerFallback(handler: OscFallbackHandlerType): void {
|
||||
this._oscParser.setHandlerFallback(handler);
|
||||
}
|
||||
|
||||
public setErrorHandler(callback: (state: IParsingState) => IParsingState): void {
|
||||
@@ -443,7 +444,6 @@ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceP
|
||||
this._params.reset();
|
||||
this._params.addParam(0); // ZDM
|
||||
this._collect = 0;
|
||||
// this._activeDcsHandler = this._dcsHandlerFb;
|
||||
this.precedingCodepoint = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
import { assert } from 'chai';
|
||||
import { OscParser, OscHandlerFactory } from 'common/parser/OscParser';
|
||||
import { OscParser, OscHandler } from 'common/parser/OscParser';
|
||||
import { StringToUtf32, utf32ToString } from 'common/input/TextDecoder';
|
||||
import { IOscHandler } from 'common/parser/Types';
|
||||
import { PAYLOAD_LIMIT } from 'common/parser/Constants';
|
||||
@@ -37,7 +37,7 @@ describe('OscParser', () => {
|
||||
beforeEach(() => {
|
||||
reports = [];
|
||||
parser = new OscParser();
|
||||
parser.setOscHandlerFallback((id, action, data) => {
|
||||
parser.setHandlerFallback((id, action, data) => {
|
||||
reports.push([id, action, data]);
|
||||
});
|
||||
});
|
||||
@@ -78,7 +78,7 @@ describe('OscParser', () => {
|
||||
});
|
||||
describe('handler registration', () => {
|
||||
it('setOscHandler', () => {
|
||||
parser.setOscHandler(1234, new TestHandler(1234, reports, 'th'));
|
||||
parser.setHandler(1234, new TestHandler(1234, reports, 'th'));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -94,8 +94,8 @@ describe('OscParser', () => {
|
||||
]);
|
||||
});
|
||||
it('clearOscHandler', () => {
|
||||
parser.setOscHandler(1234, new TestHandler(1234, reports, 'th'));
|
||||
parser.clearOscHandler(1234);
|
||||
parser.setHandler(1234, new TestHandler(1234, reports, 'th'));
|
||||
parser.clearHandler(1234);
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -111,8 +111,8 @@ describe('OscParser', () => {
|
||||
]);
|
||||
});
|
||||
it('addOscHandler', () => {
|
||||
parser.setOscHandler(1234, new TestHandler(1234, reports, 'th1'));
|
||||
parser.addOscHandler(1234, new TestHandler(1234, reports, 'th2'));
|
||||
parser.setHandler(1234, new TestHandler(1234, reports, 'th1'));
|
||||
parser.addHandler(1234, new TestHandler(1234, reports, 'th2'));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -131,8 +131,8 @@ describe('OscParser', () => {
|
||||
]);
|
||||
});
|
||||
it('addOscHandler with return false', () => {
|
||||
parser.setOscHandler(1234, new TestHandler(1234, reports, 'th1'));
|
||||
parser.addOscHandler(1234, new TestHandler(1234, reports, 'th2', true));
|
||||
parser.setHandler(1234, new TestHandler(1234, reports, 'th1'));
|
||||
parser.addHandler(1234, new TestHandler(1234, reports, 'th2', true));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -151,8 +151,8 @@ describe('OscParser', () => {
|
||||
]);
|
||||
});
|
||||
it('dispose handlers', () => {
|
||||
parser.setOscHandler(1234, new TestHandler(1234, reports, 'th1'));
|
||||
const dispo = parser.addOscHandler(1234, new TestHandler(1234, reports, 'th2', true));
|
||||
parser.setHandler(1234, new TestHandler(1234, reports, 'th1'));
|
||||
const dispo = parser.addHandler(1234, new TestHandler(1234, reports, 'th2', true));
|
||||
dispo.dispose();
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
@@ -170,7 +170,7 @@ describe('OscParser', () => {
|
||||
});
|
||||
describe('OscHandlerFactory', () => {
|
||||
it('should be called once on end(true)', () => {
|
||||
parser.setOscHandler(1234, new OscHandlerFactory(data => reports.push([1234, data])));
|
||||
parser.setHandler(1234, new OscHandler(data => reports.push([1234, data])));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -180,7 +180,7 @@ describe('OscParser', () => {
|
||||
assert.deepEqual(reports, [[1234, 'Here comes the mouse!']]);
|
||||
});
|
||||
it('should not be called on end(false)', () => {
|
||||
parser.setOscHandler(1234, new OscHandlerFactory(data => reports.push([1234, data])));
|
||||
parser.setHandler(1234, new OscHandler(data => reports.push([1234, data])));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -190,8 +190,8 @@ describe('OscParser', () => {
|
||||
assert.deepEqual(reports, []);
|
||||
});
|
||||
it('should be disposable', () => {
|
||||
parser.setOscHandler(1234, new OscHandlerFactory(data => reports.push(['one', data])));
|
||||
const dispo = parser.addOscHandler(1234, new OscHandlerFactory(data => reports.push(['two', data])));
|
||||
parser.setHandler(1234, new OscHandler(data => reports.push(['one', data])));
|
||||
const dispo = parser.addHandler(1234, new OscHandler(data => reports.push(['two', data])));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -209,8 +209,8 @@ describe('OscParser', () => {
|
||||
assert.deepEqual(reports, [['two', 'Here comes the mouse!'], ['one', 'some other data']]);
|
||||
});
|
||||
it('should respect return false', () => {
|
||||
parser.setOscHandler(1234, new OscHandlerFactory(data => reports.push(['one', data])));
|
||||
parser.addOscHandler(1234, new OscHandlerFactory(data => { reports.push(['two', data]); 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.start();
|
||||
let data = toUtf32('1234;Here comes');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -221,7 +221,7 @@ describe('OscParser', () => {
|
||||
});
|
||||
it('should work up to payload limit', function(): void {
|
||||
this.timeout(10000);
|
||||
parser.setOscHandler(1234, new OscHandlerFactory(data => reports.push([1234, data])));
|
||||
parser.setHandler(1234, new OscHandler(data => reports.push([1234, data])));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;');
|
||||
parser.put(data, 0, data.length);
|
||||
@@ -234,7 +234,7 @@ describe('OscParser', () => {
|
||||
});
|
||||
it('should abort for payload limit +1', function(): void {
|
||||
this.timeout(10000);
|
||||
parser.setOscHandler(1234, new OscHandlerFactory(data => reports.push([1234, data])));
|
||||
parser.setHandler(1234, new OscHandler(data => reports.push([1234, data])));
|
||||
parser.start();
|
||||
let data = toUtf32('1234;');
|
||||
parser.put(data, 0, data.length);
|
||||
|
||||
@@ -3,20 +3,19 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IOscHandler, IHandlerCollection, OscFallbackHandler } from 'common/parser/Types';
|
||||
import { IOscHandler, IHandlerCollection, OscFallbackHandlerType, IOscParser } from 'common/parser/Types';
|
||||
import { OscState, PAYLOAD_LIMIT } from 'common/parser/Constants';
|
||||
import { Disposable } from 'common/Lifecycle';
|
||||
import { utf32ToString } from 'common/input/TextDecoder';
|
||||
import { IDisposable } from 'common/Types';
|
||||
|
||||
|
||||
export class OscParser extends Disposable {
|
||||
export class OscParser implements IOscParser {
|
||||
private _state = OscState.START;
|
||||
private _id = -1;
|
||||
private _handlers: IHandlerCollection<IOscHandler> = Object.create(null);
|
||||
private _handlerFb: OscFallbackHandler = () => { };
|
||||
private _handlerFb: OscFallbackHandlerType = () => { };
|
||||
|
||||
public addOscHandler(ident: number, handler: IOscHandler): IDisposable {
|
||||
public addHandler(ident: number, handler: IOscHandler): IDisposable {
|
||||
if (this._handlers[ident] === undefined) {
|
||||
this._handlers[ident] = [];
|
||||
}
|
||||
@@ -31,13 +30,13 @@ export class OscParser extends Disposable {
|
||||
}
|
||||
};
|
||||
}
|
||||
public setOscHandler(ident: number, handler: IOscHandler): void {
|
||||
public setHandler(ident: number, handler: IOscHandler): void {
|
||||
this._handlers[ident] = [handler];
|
||||
}
|
||||
public clearOscHandler(ident: number): void {
|
||||
public clearHandler(ident: number): void {
|
||||
if (this._handlers[ident]) delete this._handlers[ident];
|
||||
}
|
||||
public setOscHandlerFallback(handler: OscFallbackHandler): void {
|
||||
public setHandlerFallback(handler: OscFallbackHandlerType): void {
|
||||
this._handlerFb = handler;
|
||||
}
|
||||
|
||||
@@ -168,7 +167,7 @@ export class OscParser extends Disposable {
|
||||
* Convenient class to allow attaching string based handler functions
|
||||
* as OSC handlers.
|
||||
*/
|
||||
export class OscHandlerFactory implements IOscHandler {
|
||||
export class OscHandler implements IOscHandler {
|
||||
private _data = '';
|
||||
private _hitLimit: boolean = false;
|
||||
|
||||
|
||||
Vendored
+103
-67
@@ -61,65 +61,84 @@ export interface IParsingState {
|
||||
abort: boolean;
|
||||
}
|
||||
|
||||
export interface IHandlerCollection<T> {
|
||||
[key: string]: T[];
|
||||
}
|
||||
|
||||
export type CsiHandler = (params: IParams) => boolean | void;
|
||||
export type EscHandler = () => boolean | void;
|
||||
/**
|
||||
* Command handler interfaces.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DCS handler signature for EscapeSequenceParser.
|
||||
* EscapeSequenceParser handles DCS commands via separate
|
||||
* subparsers that get hook/unhooked and can handle
|
||||
* arbitrary amount of data.
|
||||
*
|
||||
* On entering a DSC sequence `hook` is called by
|
||||
* `EscapeSequenceParser`. Use it to initialize or reset
|
||||
* states needed to handle the current DCS sequence.
|
||||
* Note: A DCS parser is only instantiated once, therefore
|
||||
* you cannot rely on the ctor to reinitialize state.
|
||||
*
|
||||
* EscapeSequenceParser will call `put` several times if the
|
||||
* parsed data got split, therefore you might have to collect
|
||||
* `data` until `unhook` is called.
|
||||
* Note: `data` is borrowed, if you cannot process the data
|
||||
* in chunks you have to copy it, doing otherwise will lead to
|
||||
* data losses or corruption.
|
||||
*
|
||||
* `unhook` marks the end of the current DCS sequence. `success`
|
||||
* indicates whether the command was aborted.
|
||||
*/
|
||||
* CSI handler types.
|
||||
* Note: `params` is borrowed.
|
||||
*/
|
||||
export type CsiHandlerType = (params: IParams) => boolean | void;
|
||||
export type CsiFallbackHandlerType = (ident: number, params: IParams) => void;
|
||||
|
||||
/**
|
||||
* DCS handler types.
|
||||
*/
|
||||
export interface IDcsHandler {
|
||||
/**
|
||||
* Called when a DCS command starts.
|
||||
* Prepare needed data structures here.
|
||||
* Note: `params` is borrowed.
|
||||
*/
|
||||
hook(params: IParams): void;
|
||||
/**
|
||||
* Incoming payload chunk.
|
||||
* Note: `params` is borrowed.
|
||||
*/
|
||||
put(data: Uint32Array, start: number, end: number): void;
|
||||
/**
|
||||
* End of DCS command. `success` indicates whether the
|
||||
* command finished normally or got aborted, thus final
|
||||
* execution of the command should depend on `success`.
|
||||
* To save memory also cleanup data structures here.
|
||||
*/
|
||||
unhook(success: boolean): void | boolean;
|
||||
}
|
||||
export type DcsFallbackHandlerType = (ident: number, action: 'HOOK' | 'PUT' | 'UNHOOK', payload?: any) => void;
|
||||
|
||||
export type DcsFallbackHandler = (ident: number, action: 'HOOK' | 'PUT' | 'UNHOOK', payload?: any) => void;
|
||||
/**
|
||||
* ESC handler types.
|
||||
*/
|
||||
export type EscHandlerType = () => boolean | void;
|
||||
export type EscFallbackHandlerType = (identifier: number) => void;
|
||||
|
||||
/**
|
||||
* EXECUTE handler types.
|
||||
*/
|
||||
export type ExecuteHandlerType = () => boolean | void;
|
||||
export type ExecuteFallbackHandlerType = (ident: number) => void;
|
||||
|
||||
/**
|
||||
* OSC handler types.
|
||||
*/
|
||||
export interface IOscHandler {
|
||||
/**
|
||||
* Announces start of this OSC command.
|
||||
* Prepare needed data structures here.
|
||||
*/
|
||||
start(): void;
|
||||
|
||||
/**
|
||||
* Incoming data chunk.
|
||||
* Note: Data is borrowed.
|
||||
*/
|
||||
put(data: Uint32Array, start: number, end: number): void;
|
||||
|
||||
/**
|
||||
* End of OSC command. `success` indicates whether the
|
||||
* command finished normally or got aborted, thus execution
|
||||
* of the command should depend on `success`.
|
||||
* To save memory cleanup data structures in `.end`.
|
||||
* command finished normally or got aborted, thus final
|
||||
* execution of the command should depend on `success`.
|
||||
* To save memory also cleanup data structures here.
|
||||
*/
|
||||
end(success: boolean): void | boolean;
|
||||
}
|
||||
export type OscFallbackHandlerType = (ident: number, action: 'START' | 'PUT' | 'END', payload?: any) => void;
|
||||
|
||||
/**
|
||||
* PRINT handler types.
|
||||
*/
|
||||
export type PrintHandlerType = (data: Uint32Array, start: number, end: number) => void;
|
||||
export type PrintFallbackHandlerType = PrintHandlerType;
|
||||
|
||||
export type OscFallbackHandler = (ident: number, action: 'START' | 'PUT' | 'END', payload?: any) => void;
|
||||
|
||||
/**
|
||||
* EscapeSequenceParser interface.
|
||||
@@ -144,65 +163,82 @@ export interface IEscapeSequenceParser extends IDisposable {
|
||||
parse(data: Uint32Array, length: number): void;
|
||||
|
||||
/**
|
||||
* Get string from ident number.
|
||||
* Get string from numercial function identifier `ident`.
|
||||
* Useful in fallback handlers which expose the low level
|
||||
* numcerical function identifier for debugging purposes.
|
||||
* Note: A full back translation to `IFunctionIdentifier`
|
||||
* is not implemented.
|
||||
*/
|
||||
identToString(ident: number): string;
|
||||
|
||||
setPrintHandler(callback: (data: Uint32Array, start: number, end: number) => void): void;
|
||||
setPrintHandler(handler: PrintHandlerType): void;
|
||||
clearPrintHandler(): void;
|
||||
|
||||
setExecuteHandler(flag: string, callback: () => void): void;
|
||||
clearExecuteHandler(flag: string): void;
|
||||
setExecuteHandlerFallback(callback: (code: number) => void): void;
|
||||
|
||||
setCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => void): void;
|
||||
clearCsiHandler(id: IFunctionIdentifier): void;
|
||||
setCsiHandlerFallback(callback: (identifier: number, params: IParams) => void): void;
|
||||
addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable;
|
||||
|
||||
setEscHandler(id: IFunctionIdentifier, callback: () => void): void;
|
||||
setEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): void;
|
||||
clearEscHandler(id: IFunctionIdentifier): void;
|
||||
setEscHandlerFallback(callback: (identifier: number) => void): void;
|
||||
addEscHandler(id: IFunctionIdentifier, handler: EscHandler): IDisposable;
|
||||
setEscHandlerFallback(handler: EscFallbackHandlerType): void;
|
||||
addEscHandler(id: IFunctionIdentifier, handler: EscHandlerType): IDisposable;
|
||||
|
||||
setOscHandler(ident: number, handler: IOscHandler): void;
|
||||
clearOscHandler(ident: number): void;
|
||||
setOscHandlerFallback(handler: OscFallbackHandler): void;
|
||||
addOscHandler(ident: number, handler: IOscHandler): IDisposable;
|
||||
setExecuteHandler(flag: string, handler: ExecuteHandlerType): void;
|
||||
clearExecuteHandler(flag: string): void;
|
||||
setExecuteHandlerFallback(handler: ExecuteFallbackHandlerType): void;
|
||||
|
||||
setCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): void;
|
||||
clearCsiHandler(id: IFunctionIdentifier): void;
|
||||
setCsiHandlerFallback(callback: CsiFallbackHandlerType): void;
|
||||
addCsiHandler(id: IFunctionIdentifier, handler: CsiHandlerType): IDisposable;
|
||||
|
||||
setDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): void;
|
||||
clearDcsHandler(id: IFunctionIdentifier): void;
|
||||
setDcsHandlerFallback(handler: DcsFallbackHandler): void;
|
||||
setDcsHandlerFallback(handler: DcsFallbackHandlerType): void;
|
||||
addDcsHandler(id: IFunctionIdentifier, handler: IDcsHandler): IDisposable;
|
||||
|
||||
setErrorHandler(callback: (state: IParsingState) => IParsingState): void;
|
||||
setOscHandler(ident: number, handler: IOscHandler): void;
|
||||
clearOscHandler(ident: number): void;
|
||||
setOscHandlerFallback(handler: OscFallbackHandlerType): void;
|
||||
addOscHandler(ident: number, handler: IOscHandler): IDisposable;
|
||||
|
||||
setErrorHandler(handler: (state: IParsingState) => IParsingState): void;
|
||||
clearErrorHandler(): void;
|
||||
}
|
||||
|
||||
export interface IOscParser extends IDisposable {
|
||||
addOscHandler(ident: number, handler: IOscHandler): IDisposable;
|
||||
setOscHandler(ident: number, handler: IOscHandler): void;
|
||||
clearOscHandler(ident: number): void;
|
||||
setOscHandlerFallback(handler: OscFallbackHandler): void;
|
||||
/**
|
||||
* Subparser interfaces.
|
||||
* The subparsers are instantiated in `EscapeSequenceParser` and
|
||||
* called during `EscapeSequenceParser.parse`.
|
||||
*/
|
||||
export interface ISubParser<T, U> extends IDisposable {
|
||||
reset(): void;
|
||||
start(): void;
|
||||
addHandler(ident: number, handler: T): IDisposable;
|
||||
setHandler(ident: number, handler: T): void;
|
||||
clearHandler(ident: number): void;
|
||||
setHandlerFallback(handler: U): void;
|
||||
put(data: Uint32Array, start: number, end: number): void;
|
||||
}
|
||||
|
||||
export interface IOscParser extends ISubParser<IOscHandler, OscFallbackHandlerType> {
|
||||
start(): void;
|
||||
end(success: boolean): void;
|
||||
}
|
||||
|
||||
export interface IDcsParser extends IDisposable {
|
||||
addDcsHandler(ident: number, handler: IDcsHandler): IDisposable;
|
||||
setDcsHandler(ident: number, handler: IDcsHandler): void;
|
||||
clearDcsHandler(ident: number): void;
|
||||
setDcsHandlerFallback(handler: DcsFallbackHandler): void;
|
||||
reset(): void;
|
||||
export interface IDcsParser extends ISubParser<IDcsHandler, DcsFallbackHandlerType> {
|
||||
hook(ident: number, params: IParams): void;
|
||||
put(data: Uint32Array, start: number, end: number): void;
|
||||
unhook(success: boolean): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to denote a specific ESC, CSI or DCS handler slot.
|
||||
* The values are used to create an integer respresentation during handler
|
||||
* regristation before passed to the subparsers as `ident`.
|
||||
* The integer translation is made to allow a faster handler access
|
||||
* in `EscapeSequenceParser.parse`.
|
||||
*/
|
||||
export interface IFunctionIdentifier {
|
||||
prefix?: string;
|
||||
intermediates?: string;
|
||||
final: string;
|
||||
}
|
||||
|
||||
export interface IHandlerCollection<T> {
|
||||
[key: string]: T[];
|
||||
}
|
||||
|
||||
+26
-17
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBuffer as IBufferApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi } from 'xterm';
|
||||
import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBuffer as IBufferApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi, IParser, IFunctionIdentifier } from 'xterm';
|
||||
import { ITerminal } from '../Types';
|
||||
import { IBufferLine } from 'common/Types';
|
||||
import { IBuffer } from 'common/buffer/Types';
|
||||
@@ -11,11 +11,12 @@ import { Terminal as TerminalCore } from '../Terminal';
|
||||
import * as Strings from '../browser/LocalizableStrings';
|
||||
import { IEvent } from 'common/EventEmitter';
|
||||
import { AddonManager } from './AddonManager';
|
||||
import { IParams, IFunctionIdentifier } from 'common/parser/Types';
|
||||
import { IParams } from 'common/parser/Types';
|
||||
|
||||
export class Terminal implements ITerminalApi {
|
||||
private _core: ITerminal;
|
||||
private _addonManager: AddonManager;
|
||||
private _parser: IParser;
|
||||
|
||||
constructor(options?: ITerminalOptions) {
|
||||
this._core = new TerminalCore(options);
|
||||
@@ -33,6 +34,12 @@ export class Terminal implements ITerminalApi {
|
||||
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; }
|
||||
|
||||
public get element(): HTMLElement { return this._core.element; }
|
||||
public get parser(): IParser {
|
||||
if (!this._parser) {
|
||||
this._parser = new ParserApi(this._core);
|
||||
}
|
||||
return this._parser;
|
||||
}
|
||||
public get textarea(): HTMLTextAreaElement { return this._core.textarea; }
|
||||
public get rows(): number { return this._core.rows; }
|
||||
public get cols(): number { return this._core.cols; }
|
||||
@@ -57,18 +64,6 @@ export class Terminal implements ITerminalApi {
|
||||
public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void {
|
||||
this._core.attachCustomKeyEventHandler(customKeyEventHandler);
|
||||
}
|
||||
public addCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable {
|
||||
return this._core.addCsiHandler(id, (params: IParams) => callback(params.toArray()));
|
||||
}
|
||||
public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable {
|
||||
return this._core.addDcsHandler(id, (data: string, params: IParams) => callback(data, params.toArray()));
|
||||
}
|
||||
public addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable {
|
||||
return this._core.addEscHandler(id, handler);
|
||||
}
|
||||
public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
|
||||
return this._core.addOscHandler(ident, callback);
|
||||
}
|
||||
public registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number {
|
||||
return this._core.registerLinkMatcher(regex, handler, options);
|
||||
}
|
||||
@@ -170,9 +165,6 @@ export class Terminal implements ITerminalApi {
|
||||
public reset(): void {
|
||||
this._core.reset();
|
||||
}
|
||||
public static applyAddon(addon: any): void {
|
||||
addon.apply(Terminal);
|
||||
}
|
||||
public loadAddon(addon: ITerminalAddon): void {
|
||||
return this._addonManager.loadAddon(this, addon);
|
||||
}
|
||||
@@ -226,3 +218,20 @@ class BufferCellApiView implements IBufferCellApi {
|
||||
public get char(): string { return this._line.getString(this._x); }
|
||||
public get width(): number { return this._line.getWidth(this._x); }
|
||||
}
|
||||
|
||||
class ParserApi implements IParser {
|
||||
constructor(private _core: ITerminal) {}
|
||||
|
||||
public addCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable {
|
||||
return this._core.addCsiHandler(id, (params: IParams) => callback(params.toArray()));
|
||||
}
|
||||
public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable {
|
||||
return this._core.addDcsHandler(id, (data: string, params: IParams) => callback(data, params.toArray()));
|
||||
}
|
||||
public addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable {
|
||||
return this._core.addEscHandler(id, handler);
|
||||
}
|
||||
public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
|
||||
return this._core.addOscHandler(ident, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,13 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
`}`;
|
||||
// Blink animation
|
||||
styles +=
|
||||
`@keyframes blink {` +
|
||||
`@keyframes blink_box_shadow {` +
|
||||
` 50% {` +
|
||||
` box-shadow: none;` +
|
||||
` }` +
|
||||
`}`;
|
||||
styles +=
|
||||
`@keyframes blink_block {` +
|
||||
` 0% {` +
|
||||
` background-color: ${this._colors.cursor.css};` +
|
||||
` color: ${this._colors.cursorAccent.css};` +
|
||||
@@ -181,8 +187,11 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
` outline: 1px solid ${this._colors.cursor.css};` +
|
||||
` outline-offset: -1px;` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS} {` +
|
||||
` animation: blink 1s step-end infinite;` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}:not(.${CURSOR_STYLE_BLOCK_CLASS}) {` +
|
||||
` animation: blink_box_shadow 1s step-end infinite;` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_BLINK_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||
` animation: blink_block 1s step-end infinite;` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||
` background-color: ${this._colors.cursor.css};` +
|
||||
|
||||
@@ -334,95 +334,6 @@ describe('InputHandler Integration Tests', function(): void {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('addCsiHandler', () => {
|
||||
it('should call custom CSI handler with js array params', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
const _customCsiHandlerParams = [];
|
||||
const _customCsiHandler = window.term.addCsiHandler({final: 'm'}, (params, collect) => {
|
||||
_customCsiHandlerParams.push(params);
|
||||
return false;
|
||||
}, '');
|
||||
`);
|
||||
await page.evaluate(`
|
||||
window.term.write('\x1b[38;5;123mparams\x1b[38:2::50:100:150msubparams');
|
||||
`);
|
||||
assert.deepEqual(await page.evaluate(`(() => _customCsiHandlerParams)();`), [[38, 5, 123], [38, [2, -1, 50, 100, 150]]]);
|
||||
});
|
||||
});
|
||||
describe('addDcsHandler', () => {
|
||||
it('should respects return value', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
const _customDcsHandlerCallStack = [];
|
||||
const _customDcsHandlerA = window.term.addDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
_customDcsHandlerCallStack.push(['A', params, data]);
|
||||
return false;
|
||||
});
|
||||
const _customDcsHandlerB = window.term.addDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
_customDcsHandlerCallStack.push(['B', params, data]);
|
||||
return true;
|
||||
});
|
||||
const _customDcsHandlerC = window.term.addDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
_customDcsHandlerCallStack.push(['C', params, data]);
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await page.evaluate(`
|
||||
window.term.write('\x1bP1;2+psome data\x1b\\\\');
|
||||
`);
|
||||
assert.deepEqual(await page.evaluate(`(() => _customDcsHandlerCallStack)();`), [['C', [1, 2], 'some data'], ['B', [1, 2], 'some data']]);
|
||||
});
|
||||
});
|
||||
describe('addEscHandler', () => {
|
||||
it('should respects return value', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
const _customEscHandlerCallStack = [];
|
||||
const _customEscHandlerA = window.term.addEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
_customEscHandlerCallStack.push('A');
|
||||
return false;
|
||||
});
|
||||
const _customEscHandlerB = window.term.addEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
_customEscHandlerCallStack.push('B');
|
||||
return true;
|
||||
});
|
||||
const _customEscHandlerC = window.term.addEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
_customEscHandlerCallStack.push('C');
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await page.evaluate(`
|
||||
window.term.write('\x1b(B');
|
||||
`);
|
||||
assert.deepEqual(await page.evaluate(`(() => _customEscHandlerCallStack)();`), ['C', 'B']);
|
||||
});
|
||||
});
|
||||
describe('addOscHandler', () => {
|
||||
it('should respects return value', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
const _customOscHandlerCallStack = [];
|
||||
const _customOscHandlerA = window.term.addOscHandler(1234, data => {
|
||||
_customOscHandlerCallStack.push(['A', data]);
|
||||
return false;
|
||||
});
|
||||
const _customOscHandlerB = window.term.addOscHandler(1234, data => {
|
||||
_customOscHandlerCallStack.push(['B', data]);
|
||||
return true;
|
||||
});
|
||||
const _customOscHandlerC = window.term.addOscHandler(1234, data => {
|
||||
_customOscHandlerCallStack.push(['C', data]);
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await page.evaluate(`
|
||||
window.term.write('\x1b]1234;some data\x07');
|
||||
`);
|
||||
assert.deepEqual(await page.evaluate(`(() => _customOscHandlerCallStack)();`), [['C', 'some data'], ['B', 'some data']]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { assert } from 'chai';
|
||||
import { ITerminalOptions } from 'xterm';
|
||||
|
||||
const APP = 'http://127.0.0.1:3000/test';
|
||||
|
||||
let browser: puppeteer.Browser;
|
||||
let page: puppeteer.Page;
|
||||
const width = 800;
|
||||
const height = 600;
|
||||
|
||||
describe('Parser Integration Tests', function(): void {
|
||||
this.timeout(20000);
|
||||
|
||||
before(async function(): Promise<any> {
|
||||
browser = await puppeteer.launch({
|
||||
headless: process.argv.indexOf('--headless') !== -1,
|
||||
slowMo: 80,
|
||||
args: [`--window-size=${width},${height}`]
|
||||
});
|
||||
page = (await browser.pages())[0];
|
||||
await page.setViewport({ width, height });
|
||||
await page.goto(APP);
|
||||
await openTerminal();
|
||||
});
|
||||
|
||||
after(() => {
|
||||
browser.close();
|
||||
});
|
||||
|
||||
describe('addCsiHandler', () => {
|
||||
it('should call custom CSI handler with js array params', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
const _customCsiHandlerParams = [];
|
||||
const _customCsiHandler = window.term.parser.addCsiHandler({final: 'm'}, (params, collect) => {
|
||||
_customCsiHandlerParams.push(params);
|
||||
return false;
|
||||
}, '');
|
||||
`);
|
||||
await page.evaluate(`
|
||||
window.term.write('\x1b[38;5;123mparams\x1b[38:2::50:100:150msubparams');
|
||||
`);
|
||||
assert.deepEqual(await page.evaluate(`(() => _customCsiHandlerParams)();`), [[38, 5, 123], [38, [2, -1, 50, 100, 150]]]);
|
||||
});
|
||||
});
|
||||
describe('addDcsHandler', () => {
|
||||
it('should respects return value', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
const _customDcsHandlerCallStack = [];
|
||||
const _customDcsHandlerA = window.term.parser.addDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
_customDcsHandlerCallStack.push(['A', params, data]);
|
||||
return false;
|
||||
});
|
||||
const _customDcsHandlerB = window.term.parser.addDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
_customDcsHandlerCallStack.push(['B', params, data]);
|
||||
return true;
|
||||
});
|
||||
const _customDcsHandlerC = window.term.parser.addDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
_customDcsHandlerCallStack.push(['C', params, data]);
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await page.evaluate(`
|
||||
window.term.write('\x1bP1;2+psome data\x1b\\\\');
|
||||
`);
|
||||
assert.deepEqual(await page.evaluate(`(() => _customDcsHandlerCallStack)();`), [['C', [1, 2], 'some data'], ['B', [1, 2], 'some data']]);
|
||||
});
|
||||
});
|
||||
describe('addEscHandler', () => {
|
||||
it('should respects return value', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
const _customEscHandlerCallStack = [];
|
||||
const _customEscHandlerA = window.term.parser.addEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
_customEscHandlerCallStack.push('A');
|
||||
return false;
|
||||
});
|
||||
const _customEscHandlerB = window.term.parser.addEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
_customEscHandlerCallStack.push('B');
|
||||
return true;
|
||||
});
|
||||
const _customEscHandlerC = window.term.parser.addEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
_customEscHandlerCallStack.push('C');
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await page.evaluate(`
|
||||
window.term.write('\x1b(B');
|
||||
`);
|
||||
assert.deepEqual(await page.evaluate(`(() => _customEscHandlerCallStack)();`), ['C', 'B']);
|
||||
});
|
||||
});
|
||||
describe('addOscHandler', () => {
|
||||
it('should respects return value', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
const _customOscHandlerCallStack = [];
|
||||
const _customOscHandlerA = window.term.parser.addOscHandler(1234, data => {
|
||||
_customOscHandlerCallStack.push(['A', data]);
|
||||
return false;
|
||||
});
|
||||
const _customOscHandlerB = window.term.parser.addOscHandler(1234, data => {
|
||||
_customOscHandlerCallStack.push(['B', data]);
|
||||
return true;
|
||||
});
|
||||
const _customOscHandlerC = window.term.parser.addOscHandler(1234, data => {
|
||||
_customOscHandlerCallStack.push(['C', data]);
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await page.evaluate(`
|
||||
window.term.write('\x1b]1234;some data\x07');
|
||||
`);
|
||||
assert.deepEqual(await page.evaluate(`(() => _customOscHandlerCallStack)();`), [['C', 'some data'], ['B', 'some data']]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
|
||||
await page.evaluate(`window.term = new Terminal(${JSON.stringify(options)})`);
|
||||
await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);
|
||||
if (options.rendererType === 'dom') {
|
||||
await page.waitForSelector('.xterm-rows');
|
||||
} else {
|
||||
await page.waitForSelector('.xterm-text-layer');
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ 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 { OscHandlerFactory } from 'common/parser/OscParser';
|
||||
import { OscHandler } from 'common/parser/OscParser';
|
||||
|
||||
|
||||
function toUtf32(s: string): Uint32Array {
|
||||
@@ -80,8 +80,8 @@ perfContext('Parser throughput - 50MB data', () => {
|
||||
parser.setExecuteHandler(C1.IND, () => {});
|
||||
parser.setExecuteHandler(C1.NEL, () => {});
|
||||
parser.setExecuteHandler(C1.HTS, () => {});
|
||||
parser.setOscHandler(0, new OscHandlerFactory((data) => {}));
|
||||
parser.setOscHandler(2, new OscHandlerFactory((data) => {}));
|
||||
parser.setOscHandler(0, new OscHandler((data) => {}));
|
||||
parser.setOscHandler(2, new OscHandler((data) => {}));
|
||||
parser.setEscHandler({final: '7'}, () => {});
|
||||
parser.setEscHandler({final: '8'}, () => {});
|
||||
parser.setEscHandler({final: 'D'}, () => {});
|
||||
|
||||
Vendored
+104
-100
@@ -207,47 +207,47 @@ declare module 'xterm' {
|
||||
*/
|
||||
export interface ITheme {
|
||||
/** The default foreground color */
|
||||
foreground?: string,
|
||||
foreground?: string;
|
||||
/** The default background color */
|
||||
background?: string,
|
||||
background?: string;
|
||||
/** The cursor color */
|
||||
cursor?: string,
|
||||
cursor?: string;
|
||||
/** The accent color of the cursor (fg color for a block cursor) */
|
||||
cursorAccent?: string,
|
||||
cursorAccent?: string;
|
||||
/** The selection background color (can be transparent) */
|
||||
selection?: string,
|
||||
selection?: string;
|
||||
/** ANSI black (eg. `\x1b[30m`) */
|
||||
black?: string,
|
||||
black?: string;
|
||||
/** ANSI red (eg. `\x1b[31m`) */
|
||||
red?: string,
|
||||
red?: string;
|
||||
/** ANSI green (eg. `\x1b[32m`) */
|
||||
green?: string,
|
||||
green?: string;
|
||||
/** ANSI yellow (eg. `\x1b[33m`) */
|
||||
yellow?: string,
|
||||
yellow?: string;
|
||||
/** ANSI blue (eg. `\x1b[34m`) */
|
||||
blue?: string,
|
||||
blue?: string;
|
||||
/** ANSI magenta (eg. `\x1b[35m`) */
|
||||
magenta?: string,
|
||||
magenta?: string;
|
||||
/** ANSI cyan (eg. `\x1b[36m`) */
|
||||
cyan?: string,
|
||||
cyan?: string;
|
||||
/** ANSI white (eg. `\x1b[37m`) */
|
||||
white?: string,
|
||||
white?: string;
|
||||
/** ANSI bright black (eg. `\x1b[1;30m`) */
|
||||
brightBlack?: string,
|
||||
brightBlack?: string;
|
||||
/** ANSI bright red (eg. `\x1b[1;31m`) */
|
||||
brightRed?: string,
|
||||
brightRed?: string;
|
||||
/** ANSI bright green (eg. `\x1b[1;32m`) */
|
||||
brightGreen?: string,
|
||||
brightGreen?: string;
|
||||
/** ANSI bright yellow (eg. `\x1b[1;33m`) */
|
||||
brightYellow?: string,
|
||||
brightYellow?: string;
|
||||
/** ANSI bright blue (eg. `\x1b[1;34m`) */
|
||||
brightBlue?: string,
|
||||
brightBlue?: string;
|
||||
/** ANSI bright magenta (eg. `\x1b[1;35m`) */
|
||||
brightMagenta?: string,
|
||||
brightMagenta?: string;
|
||||
/** ANSI bright cyan (eg. `\x1b[1;36m`) */
|
||||
brightCyan?: string,
|
||||
brightCyan?: string;
|
||||
/** ANSI bright white (eg. `\x1b[1;37m`) */
|
||||
brightWhite?: string
|
||||
brightWhite?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -386,6 +386,12 @@ declare module 'xterm' {
|
||||
*/
|
||||
readonly markers: ReadonlyArray<IMarker>;
|
||||
|
||||
/**
|
||||
* (EXPERIMENTAL) Get the parser interface to register
|
||||
* custom escape sequence handlers.
|
||||
*/
|
||||
readonly parser: IParser;
|
||||
|
||||
/**
|
||||
* Natural language strings that can be localized.
|
||||
*/
|
||||
@@ -500,70 +506,6 @@ declare module 'xterm' {
|
||||
*/
|
||||
attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void;
|
||||
|
||||
/**
|
||||
* Adds a handler for CSI escape sequences.
|
||||
* @param id Specifies the function identifier under which the callback
|
||||
* gets registered, e.g. {final: 'm'} for SGR.
|
||||
* @param callback The function to handle the sequence. The callback is
|
||||
* called with the numerical params. If the sequence has subparams the
|
||||
* array will contain subarrays with their numercial values.
|
||||
* Return true if the sequence was handled; false if we should try
|
||||
* a previous handler (set by addCsiHandler or setCsiHandler).
|
||||
* The most recently-added handler is tried first.
|
||||
* @return An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
addCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
* Adds a handler for DCS escape sequences.
|
||||
* @param id Specifies the function identifier under which the callback
|
||||
* gets registered, e.g. {intermediates: '$' final: 'q'} for DECRQSS.
|
||||
* @param callback The function to handle the sequence. Note that the
|
||||
* function will only be called once if the sequence finished sucessfully.
|
||||
* There is currently no way to intercept smaller data chunks, data chunks
|
||||
* will be stored up until the sequence is finished. Since DCS sequences
|
||||
* are not limited by the amount of data this might impose a problem for
|
||||
* big payloads. Currently xterm.js limits DCS payload to 10 MB
|
||||
* which should give enough room for most use cases.
|
||||
* The function gets the payload and numerical parameters as arguments.
|
||||
* Return true if the sequence was handled; false if we should try
|
||||
* a previous handler (set by addDcsHandler or setDcsHandler).
|
||||
* The most recently-added handler is tried first.
|
||||
* @return An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
* Adds a handler for ESC escape sequences.
|
||||
* @param id Specifies the function identifier under which the callback
|
||||
* gets registered, e.g. {intermediates: '%' final: 'G'} for
|
||||
* default charset selection.
|
||||
* @param callback The function to handle the sequence.
|
||||
* Return true if the sequence was handled; false if we should try
|
||||
* a previous handler (set by addEscHandler or setEscHandler).
|
||||
* The most recently-added handler is tried first.
|
||||
* @return An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
* Adds a handler for OSC escape sequences.
|
||||
* @param ident The number (first parameter) of the sequence.
|
||||
* @param callback The function to handle the sequence. Note that the
|
||||
* function will only be called once if the sequence finished sucessfully.
|
||||
* There is currently no way to intercept smaller data chunks, data chunks
|
||||
* will be stored up until the sequence is finished. Since OSC sequences
|
||||
* are not limited by the amount of data this might impose a problem for
|
||||
* big payloads. Currently xterm.js limits OSC payload to 10 MB
|
||||
* which should give enough room for most use cases.
|
||||
* The callback is called with OSC data string.
|
||||
* Return true if the sequence was handled; false if we should try
|
||||
* a previous handler (set by addOscHandler or setOscHandler).
|
||||
* The most recently-added handler is tried first.
|
||||
* @return An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
* (EXPERIMENTAL) Registers a link matcher, allowing custom link patterns to
|
||||
* be matched and handled.
|
||||
@@ -842,18 +784,10 @@ declare module 'xterm' {
|
||||
/**
|
||||
* Perform a full reset (RIS, aka '\x1bc').
|
||||
*/
|
||||
reset(): void
|
||||
reset(): void;
|
||||
|
||||
/**
|
||||
* Applies an addon to the Terminal prototype, making it available to all
|
||||
* newly created Terminals.
|
||||
* @param addon The addon to apply.
|
||||
* @deprecated Use the new loadAddon API/addon format.
|
||||
*/
|
||||
static applyAddon(addon: any): void;
|
||||
|
||||
/**
|
||||
* (EXPERIMENTAL) Loads an addon into this instance of xterm.js.
|
||||
* Loads an addon into this instance of xterm.js.
|
||||
* @param addon The addon to load.
|
||||
*/
|
||||
loadAddon(addon: ITerminalAddon): void;
|
||||
@@ -991,17 +925,18 @@ declare module 'xterm' {
|
||||
}
|
||||
|
||||
/**
|
||||
* Data type to register a CSI, DCS or ESC callback in the parser in the form:
|
||||
* (EXPERIMENTAL) Data type to register a CSI, DCS or ESC callback in the parser
|
||||
* in the form:
|
||||
* ESC I..I F
|
||||
* CSI Prefix P..P I..I F
|
||||
* DCS Prefix P..P I..I F data_bytes ST
|
||||
*
|
||||
*
|
||||
* with these rules/restrictions:
|
||||
* - prefix can only be used with CSI and DCS
|
||||
* - only one leading prefix byte is recognized by the parser
|
||||
* before any other parameter bytes (P..P)
|
||||
* - intermediate bytes are recognized up to 2
|
||||
*
|
||||
*
|
||||
* For custom sequences make sure to read ECMA-48 and the resources at
|
||||
* vt100.net to not clash with existing sequences or reserved address space.
|
||||
* General recommendations:
|
||||
@@ -1011,9 +946,9 @@ declare module 'xterm' {
|
||||
* thus parsers might get confused with more intermediates)
|
||||
* - test against other common emulators to check whether they escape/ignore
|
||||
* the sequence correctly
|
||||
*
|
||||
*
|
||||
* Notes: OSC command registration is handled differently (see addOscHandler)
|
||||
* APC, PM or SOS is currently not supported
|
||||
* APC, PM or SOS is currently not supported.
|
||||
*/
|
||||
export interface IFunctionIdentifier {
|
||||
/**
|
||||
@@ -1032,4 +967,73 @@ declare module 'xterm' {
|
||||
*/
|
||||
final: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* (EXPERIMENTAL) Parser interface.
|
||||
*/
|
||||
export interface IParser {
|
||||
/**
|
||||
* Adds a handler for CSI escape sequences.
|
||||
* @param id Specifies the function identifier under which the callback
|
||||
* gets registered, e.g. {final: 'm'} for SGR.
|
||||
* @param callback The function to handle the sequence. The callback is
|
||||
* called with the numerical params. If the sequence has subparams the
|
||||
* array will contain subarrays with their numercial values.
|
||||
* Return true if the sequence was handled; false if we should try
|
||||
* a previous handler (set by addCsiHandler or setCsiHandler).
|
||||
* The most recently-added handler is tried first.
|
||||
* @return An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
addCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
* Adds a handler for DCS escape sequences.
|
||||
* @param id Specifies the function identifier under which the callback
|
||||
* gets registered, e.g. {intermediates: '$' final: 'q'} for DECRQSS.
|
||||
* @param callback The function to handle the sequence. Note that the
|
||||
* function will only be called once if the sequence finished sucessfully.
|
||||
* There is currently no way to intercept smaller data chunks, data chunks
|
||||
* will be stored up until the sequence is finished. Since DCS sequences
|
||||
* are not limited by the amount of data this might impose a problem for
|
||||
* big payloads. Currently xterm.js limits DCS payload to 10 MB
|
||||
* which should give enough room for most use cases.
|
||||
* The function gets the payload and numerical parameters as arguments.
|
||||
* Return true if the sequence was handled; false if we should try
|
||||
* a previous handler (set by addDcsHandler or setDcsHandler).
|
||||
* The most recently-added handler is tried first.
|
||||
* @return An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
* Adds a handler for ESC escape sequences.
|
||||
* @param id Specifies the function identifier under which the callback
|
||||
* gets registered, e.g. {intermediates: '%' final: 'G'} for
|
||||
* default charset selection.
|
||||
* @param callback The function to handle the sequence.
|
||||
* Return true if the sequence was handled; false if we should try
|
||||
* a previous handler (set by addEscHandler or setEscHandler).
|
||||
* The most recently-added handler is tried first.
|
||||
* @return An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable;
|
||||
|
||||
/**
|
||||
* Adds a handler for OSC escape sequences.
|
||||
* @param ident The number (first parameter) of the sequence.
|
||||
* @param callback The function to handle the sequence. Note that the
|
||||
* function will only be called once if the sequence finished sucessfully.
|
||||
* There is currently no way to intercept smaller data chunks, data chunks
|
||||
* will be stored up until the sequence is finished. Since OSC sequences
|
||||
* are not limited by the amount of data this might impose a problem for
|
||||
* big payloads. Currently xterm.js limits OSC payload to 10 MB
|
||||
* which should give enough room for most use cases.
|
||||
* The callback is called with OSC data string.
|
||||
* Return true if the sequence was handled; false if we should try
|
||||
* a previous handler (set by addOscHandler or setOscHandler).
|
||||
* The most recently-added handler is tried first.
|
||||
* @return An IDisposable you can call to remove this handler.
|
||||
*/
|
||||
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user