Compare commits

...
Author SHA1 Message Date
Cursor AgentandDaniel Imms 1040b799ce Disallow single-line brace-style blocks
Set allowSingleLine to false and expand multi-line braces
across the codebase via eslint --fix.

Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
2026-05-26 17:07:35 +00:00
Cursor AgentandDaniel Imms 1c42ae9af4 Add brace-style lint rule and fix violations
Enforce 1tbs brace style with allowSingleLine per #5900.
Auto-fix else/catch brace placement across the codebase and
move a comment in font.ts that blocked else-if formatting.

Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
2026-05-26 16:42:48 +00:00
56 changed files with 1533 additions and 538 deletions
+3 -1
View File
@@ -21,7 +21,9 @@ const enum Constants {
*/
export class ImageRenderer extends Disposable implements IDisposable {
/** @deprecated Kept for backward compat — points to top layer canvas. */
public get canvas(): HTMLCanvasElement | undefined { return this._layers.get('top')?.canvas; }
public get canvas(): HTMLCanvasElement | undefined {
return this._layers.get('top')?.canvas;
}
private _layers = new Map<ImageLayer, CanvasRenderingContext2D>();
private _placeholder: HTMLCanvasElement | undefined;
private _placeholderBitmap: ImageBitmap | undefined;
+3 -1
View File
@@ -31,7 +31,9 @@ class ExtendedAttrsImage implements IExtendedAttrsImage {
}
return this._ext;
}
public set ext(value: number) { this._ext = value; }
public set ext(value: number) {
this._ext = value;
}
public get underlineStyle(): UnderlineStyle {
// Always return the URL style if it has one
+153 -51
View File
@@ -322,7 +322,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('chunked transfer responds OK on final chunk when i= on first only', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
const half = Math.floor(KITTY_BLACK_1X1_BASE64.length / 2);
@@ -542,7 +544,9 @@ test.describe('Kitty Graphics Protocol', () => {
let response = '';
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write('\x1b_Gi=31,a=q;\x1b\\');
@@ -556,7 +560,9 @@ test.describe('Kitty Graphics Protocol', () => {
let response = '';
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=42,a=q,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -581,7 +587,9 @@ test.describe('Kitty Graphics Protocol', () => {
let response = '';
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write('\x1b_Gi=60,a=q,f=100;!!!invalid!!!\x1b\\');
@@ -595,7 +603,9 @@ test.describe('Kitty Graphics Protocol', () => {
let response = '';
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write('\x1b_Gi=70,a=q,f=24;AAAA\x1b\\');
@@ -608,7 +618,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('suppresses OK response when q=1', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write(`\x1b_Gi=80,a=q,q=1,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -620,7 +632,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('suppresses OK response when q=2', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write(`\x1b_Gi=81,a=q,q=2,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -632,7 +646,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('suppresses error response when q=2', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write('\x1b_Gi=90,a=q,q=2,f=100;!!!invalid!!!\x1b\\');
@@ -645,7 +661,9 @@ test.describe('Kitty Graphics Protocol', () => {
let response = '';
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
// Per spec: "Specifying both i and I keys in any command is an error"
@@ -660,7 +678,9 @@ test.describe('Kitty Graphics Protocol', () => {
let response = '';
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
// Delete command with both i and I (no payload case)
@@ -676,7 +696,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('a=t sends EINVAL on decode error when id is specified', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write('\x1b_Gi=110,a=t,f=100;!!!invalid!!!\x1b\\');
@@ -689,7 +711,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('a=t sends no response on decode error without id', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write('\x1b_Ga=t,f=100;!!!invalid!!!\x1b\\');
@@ -701,7 +725,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('a=T sends EINVAL on decode error when id is specified', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write('\x1b_Gi=120,a=T,f=100;!!!invalid!!!\x1b\\');
@@ -714,7 +740,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('a=T sends no response on decode error without id', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write('\x1b_Ga=T,f=100;!!!invalid!!!\x1b\\');
@@ -726,7 +754,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('a=T sends EINVAL when raw pixel render fails (missing dimensions)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=130,a=T,f=24;${RAW_RGB_1X1_BLACK}\x1b\\`);
@@ -739,7 +769,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('a=T sends OK on successful render with id', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=140,a=T,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -752,7 +784,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('a=t sends OK on successful transmit with id', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=150,a=t,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -765,7 +799,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('a=t EINVAL suppressed by q=2', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write('\x1b_Gi=160,a=t,q=2,f=100;!!!invalid!!!\x1b\\');
@@ -777,7 +813,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('a=T EINVAL suppressed by q=2', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write('\x1b_Gi=170,a=T,q=2,f=100;!!!invalid!!!\x1b\\');
@@ -789,7 +827,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('a=t OK suppressed by q=1', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write(`\x1b_Gi=180,a=t,q=1,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -801,7 +841,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('a=T OK suppressed by q=1', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write(`\x1b_Gi=190,a=T,q=1,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -813,7 +855,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('a=t OK suppressed by q=2', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write(`\x1b_Gi=181,a=t,q=2,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -825,7 +869,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('a=T OK suppressed by q=2', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write(`\x1b_Gi=191,a=T,q=2,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -839,7 +885,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('query rejects t=f (file transmission)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=200,a=q,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -852,7 +900,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('query rejects t=s (shared memory)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=201,a=q,t=s,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -865,7 +915,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('query rejects t=t (temp file)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=202,a=q,t=t,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -878,7 +930,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('query accepts t=d (direct transmission)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=203,a=q,t=d,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -891,7 +945,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('query without t key defaults to direct (OK)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=204,a=q,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -904,7 +960,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('transmit rejects t=f with id (EINVAL response)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=300,a=t,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -917,7 +975,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('transmit rejects t=s with id (EINVAL response)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=301,a=t,t=s,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -930,7 +990,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('transmit rejects t=t with id (EINVAL response)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=302,a=t,t=t,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -943,7 +1005,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('transmit rejects t=f without id (no response)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Ga=t,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -956,7 +1020,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('transmit+display rejects t=f with id (EINVAL response)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=310,a=T,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -969,7 +1035,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('transmit+display rejects t=s with id (EINVAL response)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=311,a=T,t=s,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -982,7 +1050,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('transmit+display rejects t=t with id (EINVAL response)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=312,a=T,t=t,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -995,7 +1065,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('transmit+display rejects t=f without id (no response)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Ga=T,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
@@ -1026,7 +1098,9 @@ test.describe('Kitty Graphics Protocol', () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Ga=p,i=211\x1b\\`);
@@ -1039,7 +1113,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('responds ENOENT for non-existent image id', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Ga=p,i=9999\x1b\\`);
@@ -1052,7 +1128,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('without id sends no response', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write(`\x1b_Ga=p\x1b\\`);
@@ -1172,7 +1250,9 @@ test.describe('Kitty Graphics Protocol', () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write(`\x1b_Ga=p,i=220,q=1\x1b\\`);
@@ -1188,7 +1268,9 @@ test.describe('Kitty Graphics Protocol', () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write(`\x1b_Ga=p,i=221,q=2\x1b\\`);
@@ -1201,7 +1283,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('ENOENT error suppressed by q=2', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyGotResponse = false;
(window as any).term.onData(() => { (window as any).kittyGotResponse = true; });
(window as any).term.onData(() => {
(window as any).kittyGotResponse = true;
});
});
await ctx.proxy.write(`\x1b_Ga=p,i=9998,q=2\x1b\\`);
@@ -1213,7 +1297,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('ENOENT still reported when q=1 (only suppresses OK)', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Ga=p,i=9997,q=1\x1b\\`);
@@ -1268,7 +1354,9 @@ test.describe('Kitty Graphics Protocol', () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Ga=p,i=224,p=42\x1b\\`);
@@ -1803,7 +1891,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('responds with OK for valid 200x100 PNG query', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=600,a=q,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`);
@@ -1952,7 +2042,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('query returns EINVAL without dimensions', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=200,a=q,f=24;${RAW_RGB_1X1_BLACK}\x1b\\`);
await timeout(100);
@@ -1963,7 +2055,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('query returns EINVAL for insufficient pixel data', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=201,a=q,f=24,s=2,v=2;${RAW_RGB_1X1_BLACK}\x1b\\`);
await timeout(100);
@@ -1974,7 +2068,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('query returns OK for valid RGB data with correct dimensions', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=202,a=q,f=24,s=1,v=1;${RAW_RGB_1X1_RED}\x1b\\`);
await timeout(100);
@@ -2086,7 +2182,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('query returns EINVAL without dimensions', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=300,a=q,f=32;${RAW_RGBA_1X1_RED}\x1b\\`);
await timeout(100);
@@ -2097,7 +2195,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('query returns EINVAL for insufficient pixel data', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=301,a=q,f=32,s=2,v=2;${RAW_RGBA_1X1_RED}\x1b\\`);
await timeout(100);
@@ -2108,7 +2208,9 @@ test.describe('Kitty Graphics Protocol', () => {
test('query returns OK for valid RGBA data with correct dimensions', async () => {
await ctx.page.evaluate(() => {
(window as any).kittyResponse = '';
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
(window as any).term.onData((data: string) => {
(window as any).kittyResponse = data;
});
});
await ctx.proxy.write(`\x1b_Gi=302,a=q,f=32,s=1,v=1;${RAW_RGBA_1X1_RED}\x1b\\`);
await timeout(100);
+2 -3
View File
@@ -65,9 +65,8 @@ export default async function load(fontFamily: string, cacheSize: number): Promi
console.error(err.name, err.message);
}
}
}
// Latest proposal https://bugs.chromium.org/p/chromium/issues/detail?id=1312603
else if (typeof window !== 'undefined' && 'queryLocalFonts' in window) {
} else if (typeof window !== 'undefined' && 'queryLocalFonts' in window) {
// Latest proposal https://bugs.chromium.org/p/chromium/issues/detail?id=1312603
const fonts: Record<string, IFontMetadata[]> = {};
try {
const fontsIterator = await (window as any).queryLocalFonts();
@@ -46,7 +46,9 @@ describe('LigaturesAddon', () => {
});
beforeEach(() => {
onRefresh = Object.assign((..._args: any[]) => { onRefresh.called = true; onRefresh.callCount++; }, { called: false, callCount: 0 });
onRefresh = Object.assign((..._args: any[]) => {
onRefresh.called = true; onRefresh.callCount++;
}, { called: false, callCount: 0 });
term = new MockTerminal(onRefresh);
ligatureSupport.enableLigatures(term as any);
});
@@ -127,7 +129,9 @@ class MockTerminal {
public deregisterCharacterJoiner(id: number): void {
this.joiner = undefined;
}
public get options(): { [name: string]: string | number } { return this._options; }
public get options(): { [name: string]: string | number } {
return this._options;
}
public set options(options: { [name: string]: string | number }) {
for (const key in this._options) {
this._options[key] = options[key];
+6 -2
View File
@@ -64,7 +64,9 @@ export class DecorationManager extends Disposable {
public createActiveDecoration(result: ISearchResult, options: ISearchDecorationOptions): IMultiHighlight | undefined {
const decorations = this._createResultDecorations(result, options, true);
if (decorations) {
return { decorations, match: result, dispose() { dispose(decorations); } };
return { decorations, match: result, dispose() {
dispose(decorations);
} };
}
return undefined;
}
@@ -85,7 +87,9 @@ export class DecorationManager extends Disposable {
*/
private _storeDecoration(decoration: IDecoration, match: ISearchResult): void {
this._highlightedLines.add(decoration.marker.line);
this._highlightDecorations.push({ decoration, match, dispose() { decoration.dispose(); } });
this._highlightDecorations.push({ decoration, match, dispose() {
decoration.dispose();
} });
}
/**
@@ -25,7 +25,9 @@ export class SearchResultTracker extends Disposable {
private _selectedDecoration: ISelectedDecoration | undefined;
private readonly _onDidChangeResults = this._register(new Emitter<ISearchResultChangeEvent>());
public get onDidChangeResults(): IEvent<ISearchResultChangeEvent> { return this._onDidChangeResults.event; }
public get onDidChangeResults(): IEvent<ISearchResultChangeEvent> {
return this._onDidChangeResults.event;
}
/**
* Gets the current search results.
@@ -29,12 +29,20 @@ class TestSelectionService {
this._model = new SelectionModel(bufferService);
}
public get model(): SelectionModel { return this._model; }
public get model(): SelectionModel {
return this._model;
}
public get hasSelection(): boolean { return this._hasSelection; }
public get hasSelection(): boolean {
return this._hasSelection;
}
public get selectionStart(): [number, number] | undefined { return this._model.finalSelectionStart; }
public get selectionEnd(): [number, number] | undefined { return this._model.finalSelectionEnd; }
public get selectionStart(): [number, number] | undefined {
return this._model.finalSelectionStart;
}
public get selectionEnd(): [number, number] | undefined {
return this._model.finalSelectionEnd;
}
public setSelection(col: number, row: number, length: number): void {
this._model.selectionStart = [col, row];
+63 -26
View File
@@ -70,7 +70,9 @@ abstract class BaseSerializeHandler {
protected _rowEnd(row: number, isLastRow: boolean): void { }
protected _beforeSerialize(rows: number, startRow: number, endRow: number): void { }
protected _afterSerialize(): void { }
protected _serializeString(excludeFinalCursorPosition?: boolean): string { return ''; }
protected _serializeString(excludeFinalCursorPosition?: boolean): string {
return '';
}
}
function equalFg(cell1: IBufferCell | IAttributeData, cell2: IBufferCell): boolean {
@@ -291,25 +293,39 @@ class StringSerializeHandler extends BaseSerializeHandler {
} else {
if (fgChanged) {
const color = cell.getFgColor();
if (cell.isFgRGB()) { sgrSeq.push(38, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF); }
else if (cell.isFgPalette()) {
if (color >= 16) { sgrSeq.push(38, 5, color); }
else { sgrSeq.push(color & 8 ? 90 + (color & 7) : 30 + (color & 7)); }
if (cell.isFgRGB()) {
sgrSeq.push(38, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF);
} else if (cell.isFgPalette()) {
if (color >= 16) {
sgrSeq.push(38, 5, color);
} else {
sgrSeq.push(color & 8 ? 90 + (color & 7) : 30 + (color & 7));
}
} else {
sgrSeq.push(39);
}
else { sgrSeq.push(39); }
}
if (bgChanged) {
const color = cell.getBgColor();
if (cell.isBgRGB()) { sgrSeq.push(48, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF); }
else if (cell.isBgPalette()) {
if (color >= 16) { sgrSeq.push(48, 5, color); }
else { sgrSeq.push(color & 8 ? 100 + (color & 7) : 40 + (color & 7)); }
if (cell.isBgRGB()) {
sgrSeq.push(48, 2, (color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF);
} else if (cell.isBgPalette()) {
if (color >= 16) {
sgrSeq.push(48, 5, color);
} else {
sgrSeq.push(color & 8 ? 100 + (color & 7) : 40 + (color & 7));
}
} else {
sgrSeq.push(49);
}
else { sgrSeq.push(49); }
}
if (flagsChanged) {
if (cell.isInverse() !== oldCell.isInverse()) { sgrSeq.push(cell.isInverse() ? 7 : 27); }
if (cell.isBold() !== oldCell.isBold()) { sgrSeq.push(cell.isBold() ? 1 : 22); }
if (cell.isInverse() !== oldCell.isInverse()) {
sgrSeq.push(cell.isInverse() ? 7 : 27);
}
if (cell.isBold() !== oldCell.isBold()) {
sgrSeq.push(cell.isBold() ? 1 : 22);
}
if (!equalUnderline(cell, oldCell)) {
const style = cell.getUnderlineStyle();
if (style === UnderlineStyle.NONE) {
@@ -332,12 +348,24 @@ class StringSerializeHandler extends BaseSerializeHandler {
} else if (cell.isUnderline() !== oldCell.isUnderline()) {
sgrSeq.push(cell.isUnderline() ? 4 : 24);
}
if (cell.isOverline() !== oldCell.isOverline()) { sgrSeq.push(cell.isOverline() ? 53 : 55); }
if (cell.isBlink() !== oldCell.isBlink()) { sgrSeq.push(cell.isBlink() ? 5 : 25); }
if (cell.isInvisible() !== oldCell.isInvisible()) { sgrSeq.push(cell.isInvisible() ? 8 : 28); }
if (cell.isItalic() !== oldCell.isItalic()) { sgrSeq.push(cell.isItalic() ? 3 : 23); }
if (cell.isDim() !== oldCell.isDim()) { sgrSeq.push(cell.isDim() ? 2 : 22); }
if (cell.isStrikethrough() !== oldCell.isStrikethrough()) { sgrSeq.push(cell.isStrikethrough() ? 9 : 29); }
if (cell.isOverline() !== oldCell.isOverline()) {
sgrSeq.push(cell.isOverline() ? 53 : 55);
}
if (cell.isBlink() !== oldCell.isBlink()) {
sgrSeq.push(cell.isBlink() ? 5 : 25);
}
if (cell.isInvisible() !== oldCell.isInvisible()) {
sgrSeq.push(cell.isInvisible() ? 8 : 28);
}
if (cell.isItalic() !== oldCell.isItalic()) {
sgrSeq.push(cell.isItalic() ? 3 : 23);
}
if (cell.isDim() !== oldCell.isDim()) {
sgrSeq.push(cell.isDim() ? 2 : 22);
}
if (cell.isStrikethrough() !== oldCell.isStrikethrough()) {
sgrSeq.push(cell.isStrikethrough() ? 9 : 29);
}
}
}
}
@@ -646,8 +674,7 @@ export class HTMLSerializeHandler extends BaseSerializeHandler {
// For xterm headless: fallback to ansi colors
if ((_terminal as any)._core._themeService) {
this._ansiColors = (_terminal as any)._core._themeService.colors.ansi;
}
else {
} else {
this._ansiColors = DEFAULT_ANSI_COLORS;
}
}
@@ -752,8 +779,12 @@ export class HTMLSerializeHandler extends BaseSerializeHandler {
content.push('background-color: ' + bgHexColor + ';');
}
if (cell.isInverse()) { content.push('color: #000000; background-color: #BFBFBF;'); }
if (cell.isBold()) { content.push('font-weight: bold;'); }
if (cell.isInverse()) {
content.push('color: #000000; background-color: #BFBFBF;');
}
if (cell.isBold()) {
content.push('font-weight: bold;');
}
// Handle text-decoration (underline, overline, strikethrough, blink)
const decorations: string[] = [];
@@ -781,9 +812,15 @@ export class HTMLSerializeHandler extends BaseSerializeHandler {
}
}
if (cell.isInvisible()) { content.push('visibility: hidden;'); }
if (cell.isItalic()) { content.push('font-style: italic;'); }
if (cell.isDim()) { content.push('opacity: 0.5;'); }
if (cell.isInvisible()) {
content.push('visibility: hidden;');
}
if (cell.isItalic()) {
content.push('font-style: italic;');
}
if (cell.isDim()) {
content.push('opacity: 0.5;');
}
return content;
}
@@ -40,7 +40,9 @@ export class CursorBlinkStateManager {
}
}
public get isPaused(): boolean { return !(this._blinkStartTimeout || this._blinkInterval); }
public get isPaused(): boolean {
return !(this._blinkStartTimeout || this._blinkInterval);
}
public dispose(): void {
if (this._blinkInterval) {
+9 -3
View File
@@ -62,7 +62,9 @@ export class TextureAtlas implements ITextureAtlas {
// The texture that the atlas is drawn to
private _pages: AtlasPage[] = [];
public get pages(): { canvas: HTMLCanvasElement, version: number }[] { return this._pages; }
public get pages(): { canvas: HTMLCanvasElement, version: number }[] {
return this._pages;
}
// The set of atlas pages that can be written to
private _activePages: AtlasPage[] = [];
@@ -1039,10 +1041,14 @@ class AtlasPage {
public readonly ctx: CanvasRenderingContext2D;
private _usedPixels: number = 0;
public get percentageUsed(): number { return this._usedPixels / (this.canvas.width * this.canvas.height); }
public get percentageUsed(): number {
return this._usedPixels / (this.canvas.width * this.canvas.height);
}
private readonly _glyphs: IRasterizedGlyph[] = [];
public get glyphs(): ReadonlyArray<IRasterizedGlyph> { return this._glyphs; }
public get glyphs(): ReadonlyArray<IRasterizedGlyph> {
return this._glyphs;
}
public addGlyph(glyph: IRasterizedGlyph): void {
this._glyphs.push(glyph);
this._usedPixels += glyph.size.x * glyph.size.y;
+1 -2
View File
@@ -521,8 +521,7 @@ function initAddons(term: Terminal): void {
} else if (name === 'webLinks') {
controlBar.setTabVisible('addon-web-links', true);
}
}
catch {
} catch {
addon.instance = undefined;
checkbox.checked = false;
checkbox.disabled = true;
@@ -30,7 +30,9 @@ export class AddonWebFontsWindow extends BaseWindow implements IControlWindow {
this._terminal.options.lineHeight = 1.3;
this._addons.fit.instance?.fit();
setTimeout(() => this._terminal.write('\x1b[?12h\x1b]12;#776CF9\x07\x1b[38;2;119;108;249;48;2;21;8;150m\x1b[2J\x1b[2;5H**** COMMODORE 64 BASIC V2 ****\r\n\r\n 64K RAM SYSTEM 38911 BASIC BYTES FREE\r\n\r\nREADY.\r\nLOAD '), 1000);
setTimeout(() => { this._terminal.write('🤣\x1b[m\x1b[99;1H'); this._terminal.input('\r'); }, 5000);
setTimeout(() => {
this._terminal.write('🤣\x1b[m\x1b[99;1H'); this._terminal.input('\r');
}, 5000);
});
ddKongtext.appendChild(btnKongtext);
dl.appendChild(ddKongtext);
+3 -1
View File
@@ -8,7 +8,9 @@ import type { IControlWindow } from '../controlBar';
import type { Terminal } from '@xterm/xterm';
export abstract class BaseWindow implements IControlWindow {
protected get _terminal(): Terminal { return this._terminalPrivate; }
protected get _terminal(): Terminal {
return this._terminalPrivate;
}
constructor(
private _terminalPrivate: Terminal,
+1
View File
@@ -128,6 +128,7 @@ export default tseslint.config(
'no-fallthrough': 'off',
'prefer-spread': 'off',
'object-curly-spacing': ['warn', 'always'],
'brace-style': ['warn', '1tbs', { allowSingleLine: false }],
'prefer-const': 'warn',
'spaced-comment': ['warn', 'always', { markers: ['/'], exceptions: ['-'] }]
}
+19 -8
View File
@@ -71,7 +71,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
private _compositionView: HTMLElement | undefined;
private readonly _linkifier: MutableDisposable<ILinkifier2> = this._register(new MutableDisposable());
public get linkifier(): ILinkifier2 | undefined { return this._linkifier.value; }
public get linkifier(): ILinkifier2 | undefined {
return this._linkifier.value;
}
private _overviewRulerRenderer: OverviewRulerRenderer | undefined;
private _viewport: Viewport | undefined;
@@ -136,15 +138,25 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
public readonly onBell = this._onBell.event;
private _onFocus = this._register(new Emitter<void>());
public get onFocus(): IEvent<void> { return this._onFocus.event; }
public get onFocus(): IEvent<void> {
return this._onFocus.event;
}
private _onBlur = this._register(new Emitter<void>());
public get onBlur(): IEvent<void> { return this._onBlur.event; }
public get onBlur(): IEvent<void> {
return this._onBlur.event;
}
private _onA11yCharEmitter = this._register(new Emitter<string>());
public get onA11yChar(): IEvent<string> { return this._onA11yCharEmitter.event; }
public get onA11yChar(): IEvent<string> {
return this._onA11yCharEmitter.event;
}
private _onA11yTabEmitter = this._register(new Emitter<number>());
public get onA11yTab(): IEvent<number> { return this._onA11yTabEmitter.event; }
public get onA11yTab(): IEvent<number> {
return this._onA11yTabEmitter.event;
}
private _onWillOpen = this._register(new Emitter<HTMLElement>());
public get onWillOpen(): IEvent<HTMLElement> { return this._onWillOpen.event; }
public get onWillOpen(): IEvent<HTMLElement> {
return this._onWillOpen.event;
}
private readonly _onDimensionsChange = this._register(new Emitter<IRenderDimensionsApi>());
public readonly onDimensionsChange = this._onDimensionsChange.event;
@@ -563,8 +575,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
try {
this._onWillOpen.fire(this.element);
}
catch { /* fails to load addon for some reason */ }
} catch { /* fails to load addon for some reason */ }
if (!this._renderService.hasRenderer()) {
this._renderService.setRenderer(this._createRenderer());
}
+3 -1
View File
@@ -12,7 +12,9 @@ import { Emitter } from 'common/Event';
import { addDisposableListener } from 'browser/Dom';
export class Linkifier extends Disposable implements ILinkifier2 {
public get currentLink(): ILinkWithState | undefined { return this._currentLink; }
public get currentLink(): ILinkWithState | undefined {
return this._currentLink;
}
protected _currentLink: ILinkWithState | undefined;
private _mouseDownLink: ILinkWithState | undefined;
private _lastMouseEvent: MouseEvent | undefined;
+15 -5
View File
@@ -21,9 +21,15 @@ import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
import { Emitter, type IEvent } from 'common/Event';
export class TestTerminal extends CoreBrowserTerminal {
public get curAttrData(): IAttributeData { return (this as any)._inputHandler._curAttrData; }
public keyDown(ev: any): boolean | undefined { return this._keyDown(ev); }
public keyPress(ev: any): boolean { return this._keyPress(ev); }
public get curAttrData(): IAttributeData {
return (this as any)._inputHandler._curAttrData;
}
public keyDown(ev: any): boolean | undefined {
return this._keyDown(ev);
}
public keyPress(ev: any): boolean {
return this._keyPress(ev);
}
public writeP(data: string | Uint8Array): Promise<void> {
return new Promise(r => this.write(data, r));
}
@@ -213,7 +219,9 @@ export class MockTerminal implements ITerminal {
public refresh(start: number, end: number): void {
throw new Error('Method not implemented.');
}
public registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; }
public registerCharacterJoiner(handler: CharacterJoinerHandler): number {
return 0;
}
public deregisterCharacterJoiner(joinerId: number): void { }
}
@@ -377,7 +385,9 @@ export class MockCoreBrowserService implements ICoreBrowserService {
export class MockCharSizeService implements ICharSizeService {
public serviceBrand: undefined;
public get hasValidSize(): boolean { return this.width > 0 && this.height > 0; }
public get hasValidSize(): boolean {
return this.width > 0 && this.height > 0;
}
public onCharSizeChange: IEvent<void> = new Emitter<void>().event;
constructor(public width: number, public height: number) {}
public measure(): void {}
+3 -1
View File
@@ -23,7 +23,9 @@ export class CompositionHelper {
* IME. This variable determines whether the compositionText should be displayed on the UI.
*/
private _isComposing: boolean;
public get isComposing(): boolean { return this._isComposing; }
public get isComposing(): boolean {
return this._isComposing;
}
/**
* The position within the input textarea's value of the current composition.
+66 -22
View File
@@ -68,22 +68,52 @@ export class Terminal extends Disposable implements ITerminalApi {
}
}
public get onBell(): IEvent<void> { return this._core.onBell; }
public get onBinary(): IEvent<string> { return this._core.onBinary; }
public get onCursorMove(): IEvent<void> { return this._core.onCursorMove; }
public get onData(): IEvent<string> { return this._core.onData; }
public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._core.onKey; }
public get onLineFeed(): IEvent<void> { return this._core.onLineFeed; }
public get onRender(): IEvent<{ start: number, end: number }> { return this._core.onRender; }
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; }
public get onScroll(): IEvent<number> { return this._core.onScroll; }
public get onSelectionChange(): IEvent<void> { return this._core.onSelectionChange; }
public get onTitleChange(): IEvent<string> { return this._core.onTitleChange; }
public get onWriteParsed(): IEvent<void> { return this._core.onWriteParsed; }
public get onDimensionsChange(): IEvent<IRenderDimensions> { return this._core.onDimensionsChange; }
public get onBell(): IEvent<void> {
return this._core.onBell;
}
public get onBinary(): IEvent<string> {
return this._core.onBinary;
}
public get onCursorMove(): IEvent<void> {
return this._core.onCursorMove;
}
public get onData(): IEvent<string> {
return this._core.onData;
}
public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> {
return this._core.onKey;
}
public get onLineFeed(): IEvent<void> {
return this._core.onLineFeed;
}
public get onRender(): IEvent<{ start: number, end: number }> {
return this._core.onRender;
}
public get onResize(): IEvent<{ cols: number, rows: number }> {
return this._core.onResize;
}
public get onScroll(): IEvent<number> {
return this._core.onScroll;
}
public get onSelectionChange(): IEvent<void> {
return this._core.onSelectionChange;
}
public get onTitleChange(): IEvent<string> {
return this._core.onTitleChange;
}
public get onWriteParsed(): IEvent<void> {
return this._core.onWriteParsed;
}
public get onDimensionsChange(): IEvent<IRenderDimensions> {
return this._core.onDimensionsChange;
}
public get element(): HTMLElement | undefined { return this._core.element; }
public get screenElement(): HTMLElement | undefined { return this._core.screenElement; }
public get element(): HTMLElement | undefined {
return this._core.element;
}
public get screenElement(): HTMLElement | undefined {
return this._core.screenElement;
}
public get parser(): IParser {
return this._parser ??= new ParserApi(this._core);
}
@@ -91,9 +121,15 @@ export class Terminal extends Disposable implements ITerminalApi {
this._checkProposedApi();
return new UnicodeApi(this._core);
}
public get textarea(): HTMLTextAreaElement | undefined { return this._core.textarea; }
public get rows(): number { return this._core.rows; }
public get cols(): number { return this._core.cols; }
public get textarea(): HTMLTextAreaElement | undefined {
return this._core.textarea;
}
public get rows(): number {
return this._core.rows;
}
public get cols(): number {
return this._core.cols;
}
public get buffer(): IBufferNamespaceApi {
return this._buffer ??= this._register(new BufferNamespaceApi(this._core));
}
@@ -247,10 +283,18 @@ export class Terminal extends Disposable implements ITerminalApi {
public static get strings(): ILocalizableStrings {
// A wrapper is required here because esbuild prevents setting an `export let`
return {
get promptLabel(): string { return Strings.promptLabel.get(); },
set promptLabel(value: string) { Strings.promptLabel.set(value); },
get tooMuchOutput(): string { return Strings.tooMuchOutput.get(); },
set tooMuchOutput(value: string) { Strings.tooMuchOutput.set(value); }
get promptLabel(): string {
return Strings.promptLabel.get();
},
set promptLabel(value: string) {
Strings.promptLabel.set(value);
},
get tooMuchOutput(): string {
return Strings.tooMuchOutput.get();
},
set tooMuchOutput(value: string) {
Strings.tooMuchOutput.set(value);
}
};
}
+3 -1
View File
@@ -15,7 +15,9 @@ export class CharSizeService extends Disposable implements ICharSizeService {
public height: number = 0;
private _measureStrategy: IMeasureStrategy;
public get hasValidSize(): boolean { return this.width > 0 && this.height > 0; }
public get hasValidSize(): boolean {
return this.width > 0 && this.height > 0;
}
private readonly _onCharSizeChange = this._register(new Emitter<void>());
public readonly onCharSizeChange = this._onCharSizeChange.event;
+3 -1
View File
@@ -52,7 +52,9 @@ export class RenderService extends Disposable implements IRenderService {
private readonly _onRefreshRequest = this._register(new Emitter<{ start: number, end: number }>());
public readonly onRefreshRequest = this._onRefreshRequest.event;
public get dimensions(): IRenderDimensions { return this._renderer.value!.dimensions; }
public get dimensions(): IRenderDimensions {
return this._renderer.value!.dimensions;
}
constructor(
private _rowCount: number,
+15 -5
View File
@@ -27,13 +27,23 @@ class TestSelectionService extends SelectionService {
super(null!, null!, null!, bufferService, new MockCoreService(), new MockMouseService(), optionsService, renderService, new MockCoreBrowserService());
}
public get model(): SelectionModel { return this._model; }
public get model(): SelectionModel {
return this._model;
}
public set selectionMode(mode: SelectionMode) { this._activeSelectionMode = mode; }
public set selectionMode(mode: SelectionMode) {
this._activeSelectionMode = mode;
}
public selectLineAt(line: number): void { this._selectLineAt(line); }
public selectWordAt(coords: [number, number]): void { this._selectWordAt(coords, true); }
public areCoordsInSelection(coords: [number, number], start: [number, number], end: [number, number]): boolean { return this._areCoordsInSelection(coords, start, end); }
public selectLineAt(line: number): void {
this._selectLineAt(line);
}
public selectWordAt(coords: [number, number]): void {
this._selectWordAt(coords, true);
}
public areCoordsInSelection(coords: [number, number], start: [number, number], end: [number, number]): boolean {
return this._areCoordsInSelection(coords, start, end);
}
// Disable DOM interaction
public enable(): void {}
+6 -2
View File
@@ -181,8 +181,12 @@ export class SelectionService extends Disposable implements ISelectionService {
this._enabled = true;
}
public get selectionStart(): [number, number] | undefined { return this._model.finalSelectionStart; }
public get selectionEnd(): [number, number] | undefined { return this._model.finalSelectionEnd; }
public get selectionStart(): [number, number] | undefined {
return this._model.finalSelectionStart;
}
public get selectionEnd(): [number, number] | undefined {
return this._model.finalSelectionEnd;
}
/**
* Gets whether there is an active text selection.
+3 -1
View File
@@ -38,7 +38,9 @@ export class ThemeService extends Disposable implements IThemeService {
private _halfContrastCache: IColorContrastCache = new ColorContrastCache();
private _restoreColors!: IRestoreColorSet;
public get colors(): ReadonlyColorSet { return this._colors; }
public get colors(): ReadonlyColorSet {
return this._colors;
}
private readonly _onChangeColors = this._register(new Emitter<ReadonlyColorSet>());
public readonly onChangeColors = this._onChangeColors.event;
+1 -2
View File
@@ -132,8 +132,7 @@ export namespace css {
$ctx.globalCompositeOperation = 'copy';
$litmusColor = $ctx.createLinearGradient(0, 0, 1, 1);
}
}
catch {
} catch {
// noop
}
+12 -4
View File
@@ -88,10 +88,18 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
return this._onScrollApi.event;
}
public get cols(): number { return this._bufferService.cols; }
public get rows(): number { return this._bufferService.rows; }
public get buffers(): IBufferSet { return this._bufferService.buffers; }
public get options(): Required<ITerminalOptions> { return this.optionsService.options; }
public get cols(): number {
return this._bufferService.cols;
}
public get rows(): number {
return this._bufferService.rows;
}
public get buffers(): IBufferSet {
return this._bufferService.buffers;
}
public get options(): Required<ITerminalOptions> {
return this.optionsService.options;
}
public set options(options: ITerminalOptions) {
for (const key in options) {
this.optionsService.options[key] = options[key];
+12 -4
View File
@@ -15,14 +15,18 @@ describe('Emitter', () => {
it('should fire with 1 listener', () => {
const emitter = new Emitter<number>();
let received: number | undefined;
emitter.event(e => { received = e; });
emitter.event(e => {
received = e;
});
emitter.fire(42);
assert.strictEqual(received, 42);
});
it('should fire with 1 listener using thisArgs', () => {
const emitter = new Emitter<number>();
const obj = { value: 0, handler(e: number) { this.value = e; } };
const obj = { value: 0, handler(e: number) {
this.value = e;
} };
emitter.event(obj.handler, obj);
emitter.fire(42);
assert.strictEqual(obj.value, 42);
@@ -54,7 +58,9 @@ describe('Emitter', () => {
it('should not fire after dispose', () => {
const emitter = new Emitter<number>();
let called = false;
emitter.event(() => { called = true; });
emitter.event(() => {
called = true;
});
emitter.dispose();
emitter.fire(42);
assert.strictEqual(called, false);
@@ -63,7 +69,9 @@ describe('Emitter', () => {
it('should allow disposing a listener', () => {
const emitter = new Emitter<number>();
let count = 0;
const disposable = emitter.event(() => { count++; });
const disposable = emitter.event(() => {
count++;
});
emitter.fire(1);
disposable.dispose();
emitter.fire(2);
+12 -4
View File
@@ -39,9 +39,15 @@ function getLines(bufferService: IBufferService, limit: number = bufferService.r
}
class TestInputHandler extends InputHandler {
public get curAttrData(): IAttributeData { return (this as any)._curAttrData; }
public get windowTitleStack(): string[] { return this._windowTitleStack; }
public get iconNameStack(): string[] { return this._iconNameStack; }
public get curAttrData(): IAttributeData {
return (this as any)._curAttrData;
}
public get windowTitleStack(): string[] {
return this._windowTitleStack;
}
public get iconNameStack(): string[] {
return this._iconNameStack;
}
/**
* Promise based parse call to await the full resolve of given input data.
@@ -2511,7 +2517,9 @@ describe('InputHandler', () => {
bufferService = new BufferService(optionsService, new MockLogService());
bufferService.resize(80, 30);
coreService = new CoreService(bufferService, new MockLogService(), optionsService);
coreService.onData(data => { console.log(data); });
coreService.onData(data => {
console.log(data);
});
inputHandler = new TestInputHandler(bufferService, new MockCharsetService(), coreService, new MockLogService(), optionsService, new MockOscLinkService(), new MockMouseStateService(), new MockUnicodeService());
});
+7 -4
View File
@@ -107,7 +107,9 @@ export class InputHandler extends Disposable implements IInputHandler {
protected _iconNameStack: string[] = [];
private _curAttrData: IAttributeData = DEFAULT_ATTR_DATA.clone();
public getAttrData(): IAttributeData { return this._curAttrData; }
public getAttrData(): IAttributeData {
return this._curAttrData;
}
private _eraseAttrDataInternal: IAttributeData = DEFAULT_ATTR_DATA.clone();
private _activeBuffer: IBuffer;
@@ -283,7 +285,9 @@ export class InputHandler extends Disposable implements IInputHandler {
* OSC handler
*/
// 0 - icon name + title
this._parser.registerOscHandler(0, new OscHandler(data => { this.setTitle(data); this.setIconName(data); return true; }));
this._parser.registerOscHandler(0, new OscHandler(data => {
this.setTitle(data); this.setIconName(data); return true;
}));
// 1 - icon name
this._parser.registerOscHandler(1, new OscHandler(data => this.setIconName(data)));
// 2 - title
@@ -1268,8 +1272,7 @@ export class InputHandler extends Disposable implements IInputHandler {
for (; j >= 0; j--) {
this._bufferService.scroll(this._eraseAttrData());
}
}
else {
} else {
j = this._bufferService.rows;
this._dirtyRowTracker.markDirty(j - 1);
while (j--) {
+18 -6
View File
@@ -30,7 +30,9 @@ export const NULL_CELL_DATA = Object.freeze(createCellData(DEFAULT_ATTR, NULL_CE
export class MockBufferService implements IBufferService {
public serviceBrand: any;
public get buffer(): IBuffer { return this.buffers.active; }
public get buffer(): IBuffer {
return this.buffers.active;
}
public buffers: IBufferSet = {} as any;
public onResize: IEvent<IBufferResizeEvent> = new Emitter<IBufferResizeEvent>().event;
public onScroll: IEvent<number> = new Emitter<number>().event;
@@ -83,10 +85,16 @@ export class MockMouseStateService implements IMouseStateService {
public addProtocol(name: string): void { }
public reset(): void { }
public onProtocolChange: IEvent<CoreMouseEventType> = new Emitter<CoreMouseEventType>().event;
public restrictMouseEvent(event: ICoreMouseEvent): boolean { return true; }
public encodeMouseEvent(event: ICoreMouseEvent): string { return ''; }
public restrictMouseEvent(event: ICoreMouseEvent): boolean {
return true;
}
public encodeMouseEvent(event: ICoreMouseEvent): string {
return '';
}
public setCustomWheelEventHandler(customWheelEventHandler: ((event: WheelEvent) => boolean) | undefined): void { }
public allowCustomWheelEvent(ev: WheelEvent): boolean { return true; }
public allowCustomWheelEvent(ev: WheelEvent): boolean {
return true;
}
}
export class MockCharsetService implements ICharsetService {
@@ -233,10 +241,14 @@ export class MockUnicodeService implements IUnicodeService {
export class MockDecorationService implements IDecorationService {
public serviceBrand: any;
public get decorations(): IterableIterator<IInternalDecoration> { return [].values(); }
public get decorations(): IterableIterator<IInternalDecoration> {
return [].values();
}
public onDecorationRegistered = new Emitter<IInternalDecoration>().event;
public onDecorationRemoved = new Emitter<IInternalDecoration>().event;
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined { return undefined; }
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
return undefined;
}
public reset(): void { }
public forEachDecorationAtCell(x: number, line: number, layer: 'bottom' | 'top' | undefined, callback: (decoration: IInternalDecoration) => void): void { }
public dispose(): void { }
+57 -19
View File
@@ -33,32 +33,68 @@ export class AttributeData implements IAttributeData {
public extended: IExtendedAttrs = new ExtendedAttrs();
// flags
public isInverse(): number { return this.fg & FgFlags.INVERSE; }
public isBold(): number { return this.fg & FgFlags.BOLD; }
public isInverse(): number {
return this.fg & FgFlags.INVERSE;
}
public isBold(): number {
return this.fg & FgFlags.BOLD;
}
public isUnderline(): number {
if (this.hasExtendedAttrs() && this.extended.underlineStyle !== UnderlineStyle.NONE) {
return 1;
}
return this.fg & FgFlags.UNDERLINE;
}
public isBlink(): number { return this.fg & FgFlags.BLINK; }
public isInvisible(): number { return this.fg & FgFlags.INVISIBLE; }
public isItalic(): number { return this.bg & BgFlags.ITALIC; }
public isDim(): number { return this.bg & BgFlags.DIM; }
public isStrikethrough(): number { return this.fg & FgFlags.STRIKETHROUGH; }
public isProtected(): number { return this.bg & BgFlags.PROTECTED; }
public isOverline(): number { return this.bg & BgFlags.OVERLINE; }
public isBlink(): number {
return this.fg & FgFlags.BLINK;
}
public isInvisible(): number {
return this.fg & FgFlags.INVISIBLE;
}
public isItalic(): number {
return this.bg & BgFlags.ITALIC;
}
public isDim(): number {
return this.bg & BgFlags.DIM;
}
public isStrikethrough(): number {
return this.fg & FgFlags.STRIKETHROUGH;
}
public isProtected(): number {
return this.bg & BgFlags.PROTECTED;
}
public isOverline(): number {
return this.bg & BgFlags.OVERLINE;
}
// color modes
public getFgColorMode(): number { return this.fg & Attributes.CM_MASK; }
public getBgColorMode(): number { return this.bg & Attributes.CM_MASK; }
public isFgRGB(): boolean { return (this.fg & Attributes.CM_MASK) === Attributes.CM_RGB; }
public isBgRGB(): boolean { return (this.bg & Attributes.CM_MASK) === Attributes.CM_RGB; }
public isFgPalette(): boolean { return (this.fg & Attributes.CM_MASK) === Attributes.CM_P16 || (this.fg & Attributes.CM_MASK) === Attributes.CM_P256; }
public isBgPalette(): boolean { return (this.bg & Attributes.CM_MASK) === Attributes.CM_P16 || (this.bg & Attributes.CM_MASK) === Attributes.CM_P256; }
public isFgDefault(): boolean { return (this.fg & Attributes.CM_MASK) === 0; }
public isBgDefault(): boolean { return (this.bg & Attributes.CM_MASK) === 0; }
public isAttributeDefault(): boolean { return this.fg === 0 && this.bg === 0; }
public getFgColorMode(): number {
return this.fg & Attributes.CM_MASK;
}
public getBgColorMode(): number {
return this.bg & Attributes.CM_MASK;
}
public isFgRGB(): boolean {
return (this.fg & Attributes.CM_MASK) === Attributes.CM_RGB;
}
public isBgRGB(): boolean {
return (this.bg & Attributes.CM_MASK) === Attributes.CM_RGB;
}
public isFgPalette(): boolean {
return (this.fg & Attributes.CM_MASK) === Attributes.CM_P16 || (this.fg & Attributes.CM_MASK) === Attributes.CM_P256;
}
public isBgPalette(): boolean {
return (this.bg & Attributes.CM_MASK) === Attributes.CM_P16 || (this.bg & Attributes.CM_MASK) === Attributes.CM_P256;
}
public isFgDefault(): boolean {
return (this.fg & Attributes.CM_MASK) === 0;
}
public isBgDefault(): boolean {
return (this.bg & Attributes.CM_MASK) === 0;
}
public isAttributeDefault(): boolean {
return this.fg === 0 && this.bg === 0;
}
// colors
public getFgColor(): number {
@@ -147,7 +183,9 @@ export class ExtendedAttrs implements IExtendedAttrs {
}
return this._ext;
}
public set ext(value: number) { this._ext = value; }
public set ext(value: number) {
this._ext = value;
}
public get underlineStyle(): UnderlineStyle {
// Always return the URL style if it has one
+4 -8
View File
@@ -62,8 +62,7 @@ export class CellData extends AttributeData implements ICellData {
// surrogates and combined strings need special treatment
if (value[CHAR_DATA_CHAR_INDEX].length > 2) {
combined = true;
}
else if (value[CHAR_DATA_CHAR_INDEX].length === 2) {
} else if (value[CHAR_DATA_CHAR_INDEX].length === 2) {
const code = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0);
// if the 2-char string is a surrogate create single codepoint
// everything else is combined
@@ -71,16 +70,13 @@ export class CellData extends AttributeData implements ICellData {
const second = value[CHAR_DATA_CHAR_INDEX].charCodeAt(1);
if (0xDC00 <= second && second <= 0xDFFF) {
this.content = ((code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000) | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT);
}
else {
} else {
combined = true;
}
}
else {
} else {
combined = true;
}
}
else {
} else {
this.content = value[CHAR_DATA_CHAR_INDEX].charCodeAt(0) | (value[CHAR_DATA_WIDTH_INDEX] << Content.WIDTH_SHIFT);
}
if (combined) {
+3 -1
View File
@@ -14,7 +14,9 @@ export class Marker implements IMarker {
private readonly _disposables: IDisposable[] = [];
private readonly _id: number = Marker._nextId++;
public get id(): number { return this._id; }
public get id(): number {
return this._id;
}
private readonly _onDispose = this.register(new Emitter<void>());
public readonly onDispose = this._onDispose.event;
+3 -6
View File
@@ -58,22 +58,19 @@ export function evaluateKeyboardEvent(
} else {
result.key = C0.ESC + '[A';
}
}
else if (ev.key === 'UIKeyInputLeftArrow') {
} else if (ev.key === 'UIKeyInputLeftArrow') {
if (applicationCursorMode) {
result.key = C0.ESC + 'OD';
} else {
result.key = C0.ESC + '[D';
}
}
else if (ev.key === 'UIKeyInputRightArrow') {
} else if (ev.key === 'UIKeyInputRightArrow') {
if (applicationCursorMode) {
result.key = C0.ESC + 'OC';
} else {
result.key = C0.ESC + '[C';
}
}
else if (ev.key === 'UIKeyInputDownArrow') {
} else if (ev.key === 'UIKeyInputDownArrow') {
if (applicationCursorMode) {
result.key = C0.ESC + 'OB';
} else {
+1 -2
View File
@@ -230,8 +230,7 @@ export class Utf8ToUtf32 {
&& !((byte1 = input[i]) & 0x80)
&& !((byte2 = input[i + 1]) & 0x80)
&& !((byte3 = input[i + 2]) & 0x80)
&& !((byte4 = input[i + 3]) & 0x80))
{
&& !((byte4 = input[i + 3]) & 0x80)) {
target[size++] = byte1;
target[size++] = byte2;
target[size++] = byte3;
+57 -19
View File
@@ -24,14 +24,20 @@ describe('WriteBuffer', () => {
beforeEach(() => {
stack = [];
cbStack = [];
wb = new WriteBuffer(data => { stack.push(data); });
wb = new WriteBuffer(data => {
stack.push(data);
});
});
describe('write input', () => {
it('string', done => {
wb.write('a._');
wb.write('b.x', () => { cbStack.push('b'); });
wb.write('b.x', () => {
cbStack.push('b');
});
wb.write('c._');
wb.write('d.x', () => { cbStack.push('d'); });
wb.write('d.x', () => {
cbStack.push('d');
});
wb.write('e', () => {
assert.deepEqual(stack, ['a._', 'b.x', 'c._', 'd.x', 'e']);
assert.deepEqual(cbStack, ['b', 'd']);
@@ -40,9 +46,13 @@ describe('WriteBuffer', () => {
});
it('bytes', done => {
wb.write(toBytes('a._'));
wb.write(toBytes('b.x'), () => { cbStack.push('b'); });
wb.write(toBytes('b.x'), () => {
cbStack.push('b');
});
wb.write(toBytes('c._'));
wb.write(toBytes('d.x'), () => { cbStack.push('d'); });
wb.write(toBytes('d.x'), () => {
cbStack.push('d');
});
wb.write(toBytes('e'), () => {
assert.deepEqual(stack.map(val => typeof val === 'string' ? '' : fromBytes(val)), ['a._', 'b.x', 'c._', 'd.x', 'e']);
assert.deepEqual(cbStack, ['b', 'd']);
@@ -51,9 +61,13 @@ describe('WriteBuffer', () => {
});
it('string/bytes mixed', done => {
wb.write('a._');
wb.write('b.x', () => { cbStack.push('b'); });
wb.write('b.x', () => {
cbStack.push('b');
});
wb.write(toBytes('c._'));
wb.write(toBytes('d.x'), () => { cbStack.push('d'); });
wb.write(toBytes('d.x'), () => {
cbStack.push('d');
});
wb.write(toBytes('e'), () => {
assert.deepEqual(stack.map(val => typeof val === 'string' ? val : fromBytes(val)), ['a._', 'b.x', 'c._', 'd.x', 'e']);
assert.deepEqual(cbStack, ['b', 'd']);
@@ -61,10 +75,18 @@ describe('WriteBuffer', () => {
});
});
it('write callback works for empty chunks', done => {
wb.write('a', () => { cbStack.push('a'); });
wb.write('', () => { cbStack.push('b'); });
wb.write(toBytes('c'), () => { cbStack.push('c'); });
wb.write(new Uint8Array(0), () => { cbStack.push('d'); });
wb.write('a', () => {
cbStack.push('a');
});
wb.write('', () => {
cbStack.push('b');
});
wb.write(toBytes('c'), () => {
cbStack.push('c');
});
wb.write(new Uint8Array(0), () => {
cbStack.push('d');
});
wb.write('e', () => {
assert.deepEqual(stack.map(val => typeof val === 'string' ? val : fromBytes(val)), ['a', '', 'c', '', 'e']);
assert.deepEqual(cbStack, ['a', 'b', 'c', 'd']);
@@ -72,13 +94,21 @@ describe('WriteBuffer', () => {
});
});
it('writeSync', done => {
wb.write('a', () => { cbStack.push('a'); });
wb.write('b', () => { cbStack.push('b'); });
wb.write('c', () => { cbStack.push('c'); });
wb.write('a', () => {
cbStack.push('a');
});
wb.write('b', () => {
cbStack.push('b');
});
wb.write('c', () => {
cbStack.push('c');
});
wb.writeSync('d');
assert.deepEqual(stack, ['a', 'b', 'c', 'd']);
assert.deepEqual(cbStack, ['a', 'b', 'c']);
wb.write('x', () => { cbStack.push('x'); });
wb.write('x', () => {
cbStack.push('x');
});
wb.write('', () => {
assert.deepEqual(stack, ['a', 'b', 'c', 'd', 'x', '']);
assert.deepEqual(cbStack, ['a', 'b', 'c', 'x']);
@@ -107,13 +137,21 @@ describe('WriteBuffer', () => {
assert.equal(last, '11'); // 1 + 10 sub calls = 11
});
it('flushSync processes all pending writes', done => {
wb.write('a', () => { cbStack.push('a'); });
wb.write('b', () => { cbStack.push('b'); });
wb.write('c', () => { cbStack.push('c'); });
wb.write('a', () => {
cbStack.push('a');
});
wb.write('b', () => {
cbStack.push('b');
});
wb.write('c', () => {
cbStack.push('c');
});
wb.flushSync();
assert.deepEqual(stack, ['a', 'b', 'c']);
assert.deepEqual(cbStack, ['a', 'b', 'c']);
wb.write('x', () => { cbStack.push('x'); });
wb.write('x', () => {
cbStack.push('x');
});
wb.write('', () => {
assert.deepEqual(stack, ['a', 'b', 'c', 'x', '']);
assert.deepEqual(cbStack, ['a', 'b', 'c', 'x']);
+3 -1
View File
@@ -244,7 +244,9 @@ export class WriteBuffer extends Disposable {
// 2. spawn a promise immediately resolving to `true`
// (executed on the same queue, thus properly aligned before continuation happens)
result.catch(err => {
queueMicrotask(() => {throw err;});
queueMicrotask(() => {
throw err;
});
return Promise.resolve(false);
}).then(continuation);
return;
+42 -14
View File
@@ -185,7 +185,9 @@ describe('ApcParser', () => {
});
it('should be called once on end(true)', () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => { reports.push(data); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => {
reports.push(data); return true;
}));
parser.start(identifier({intermediates: '+', final: 'p'}));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -195,7 +197,9 @@ describe('ApcParser', () => {
assert.deepEqual(reports, ['Here comes the mouse!']);
});
it('should not be called on end(false)', () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => { reports.push(data); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => {
reports.push(data); return true;
}));
parser.start(identifier({intermediates: '+', final: 'p'}));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -205,8 +209,12 @@ describe('ApcParser', () => {
assert.deepEqual(reports, []);
});
it('should be disposable', () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => { reports.push(['one', data]); return true; }));
const dispo = parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => { reports.push(['two', data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => {
reports.push(['one', data]); return true;
}));
const dispo = parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => {
reports.push(['two', data]); return true;
}));
parser.start(identifier({intermediates: '+', final: 'p'}));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -224,8 +232,12 @@ describe('ApcParser', () => {
assert.deepEqual(reports, [['two', 'Here comes the mouse!'], ['one', 'some other data']]);
});
it('should respect return false', () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => { reports.push(['one', data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => { reports.push(['two', data]); return false; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => {
reports.push(['one', data]); return true;
}));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => {
reports.push(['two', data]); return false;
}));
parser.start(identifier({intermediates: '+', final: 'p'}));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -236,7 +248,9 @@ describe('ApcParser', () => {
});
it('should work up to payload limit', function(): void {
this.timeout(30000);
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => { reports.push(data); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => {
reports.push(data); return true;
}));
parser.start(identifier({intermediates: '+', final: 'p'}));
const data = toUtf32('A'.repeat(CHUNK_SIZE));
for (let i = 0; i < TEST_PAYLOAD_LIMIT; i += CHUNK_SIZE) {
@@ -247,7 +261,9 @@ describe('ApcParser', () => {
});
it('should abort for payload limit +1', function(): void {
this.timeout(30000);
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => { reports.push(data); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(data => {
reports.push(data); return true;
}));
parser.start(identifier({intermediates: '+', final: 'p'}));
let data = toUtf32('A'.repeat(CHUNK_SIZE));
for (let i = 0; i < TEST_PAYLOAD_LIMIT; i += CHUNK_SIZE) {
@@ -408,7 +424,9 @@ describe('ApcParser - async tests', () => {
});
describe('ApcHandlerFactory', () => {
it('should be called once on end(true)', async () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(async data => { reports.push(data); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(async data => {
reports.push(data); return true;
}));
parser.start(identifier({intermediates: '+', final: 'p'}));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -418,7 +436,9 @@ describe('ApcParser - async tests', () => {
assert.deepEqual(reports, ['Here comes the mouse!']);
});
it('should not be called on end(false)', async () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(async data => { reports.push(data); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(async data => {
reports.push(data); return true;
}));
parser.start(identifier({intermediates: '+', final: 'p'}));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -428,8 +448,12 @@ describe('ApcParser - async tests', () => {
assert.deepEqual(reports, []);
});
it('should be disposable', async () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(async data => { reports.push(['one', data]); return true; }));
const dispo = parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(async data => { reports.push(['two', data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(async data => {
reports.push(['one', data]); return true;
}));
const dispo = parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(async data => {
reports.push(['two', data]); return true;
}));
parser.start(identifier({intermediates: '+', final: 'p'}));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -447,8 +471,12 @@ describe('ApcParser - async tests', () => {
assert.deepEqual(reports, [['two', 'Here comes the mouse!'], ['one', 'some other data']]);
});
it('should respect return false', async () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(async data => { reports.push(['one', data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(async data => { reports.push(['two', data]); return false; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(async data => {
reports.push(['one', data]); return true;
}));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new ApcHandler(async data => {
reports.push(['two', data]); return false;
}));
parser.start(identifier({intermediates: '+', final: 'p'}));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
+42 -14
View File
@@ -191,7 +191,9 @@ describe('DcsParser', () => {
});
it('should be called once on end(true)', () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push([params.toArray(), data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => {
reports.push([params.toArray(), data]); return true;
}));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -201,7 +203,9 @@ describe('DcsParser', () => {
assert.deepEqual(reports, [[[1, 2, 3], 'Here comes the mouse!']]);
});
it('should not be called on end(false)', () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push([params.toArray(), data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => {
reports.push([params.toArray(), data]); return true;
}));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -211,8 +215,12 @@ describe('DcsParser', () => {
assert.deepEqual(reports, []);
});
it('should be disposable', () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['one', params.toArray(), data]); return true; }));
const dispo = parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['two', params.toArray(), data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => {
reports.push(['one', params.toArray(), data]); return true;
}));
const dispo = parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => {
reports.push(['two', params.toArray(), data]); return true;
}));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -230,8 +238,12 @@ 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.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['one', params.toArray(), data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push(['two', params.toArray(), data]); return false; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => {
reports.push(['one', params.toArray(), data]); return true;
}));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => {
reports.push(['two', params.toArray(), data]); return false;
}));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -242,7 +254,9 @@ describe('DcsParser', () => {
});
it('should work up to payload limit', function(): void {
this.timeout(30000);
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push([params.toArray(), data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => {
reports.push([params.toArray(), data]); return true;
}));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
const data = toUtf32('A'.repeat(CHUNK_SIZE));
for (let i = 0; i < TEST_PAYLOAD_LIMIT; i += CHUNK_SIZE) {
@@ -253,7 +267,9 @@ describe('DcsParser', () => {
});
it('should abort for payload limit +1', function(): void {
this.timeout(30000);
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => { reports.push([params.toArray(), data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler((data, params) => {
reports.push([params.toArray(), data]); return true;
}));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('A'.repeat(CHUNK_SIZE));
for (let i = 0; i < TEST_PAYLOAD_LIMIT; i += CHUNK_SIZE) {
@@ -419,7 +435,9 @@ describe('DcsParser - async tests', () => {
});
describe('DcsHandlerFactory', () => {
it('should be called once on end(true)', async () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler(async (data, params) => { reports.push([params.toArray(), data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler(async (data, params) => {
reports.push([params.toArray(), data]); return true;
}));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -429,7 +447,9 @@ describe('DcsParser - async tests', () => {
assert.deepEqual(reports, [[[1, 2, 3], 'Here comes the mouse!']]);
});
it('should not be called on end(false)', async () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler(async (data, params) => { reports.push([params.toArray(), data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler(async (data, params) => {
reports.push([params.toArray(), data]); return true;
}));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -439,8 +459,12 @@ describe('DcsParser - async tests', () => {
assert.deepEqual(reports, []);
});
it('should be disposable', async () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler(async (data, params) => { reports.push(['one', params.toArray(), data]); return true; }));
const dispo = parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler(async (data, params) => { reports.push(['two', params.toArray(), data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler(async (data, params) => {
reports.push(['one', params.toArray(), data]); return true;
}));
const dispo = parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler(async (data, params) => {
reports.push(['two', params.toArray(), data]); return true;
}));
parser.hook(identifier({intermediates: '+', final: 'p'}), Params.fromArray([1, 2, 3]));
let data = toUtf32('Here comes');
parser.put(data, 0, data.length);
@@ -458,8 +482,12 @@ describe('DcsParser - async tests', () => {
assert.deepEqual(reports, [['two', [1, 2, 3], 'Here comes the mouse!'], ['one', [1, 2, 3], 'some other data']]);
});
it('should respect return false', async () => {
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler(async (data, params) => { reports.push(['one', params.toArray(), data]); return true; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler(async (data, params) => { reports.push(['two', params.toArray(), data]); return false; }));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler(async (data, params) => {
reports.push(['one', params.toArray(), data]); return true;
}));
parser.registerHandler(identifier({intermediates: '+', final: 'p'}), new DcsHandler(async (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);
+438 -146
View File
@@ -1451,49 +1451,83 @@ describe('EscapeSequenceParser', () => {
});
describe('ESC custom handlers', () => {
it('prevent fallback', () => {
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('default - %G'); return true; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('custom - %G'); return true; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('default - %G'); return true;
});
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('custom - %G'); return true;
});
parse(parser2, INPUT);
assert.deepEqual(esc, ['custom - %G']);
});
it('allow fallback', () => {
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('default - %G'); return true; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('custom - %G'); return false; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('default - %G'); return true;
});
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('custom - %G'); return false;
});
parse(parser2, INPUT);
assert.deepEqual(esc, ['custom - %G', 'default - %G']);
});
it('Multiple custom handlers fallback once', () => {
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('default - %G'); return true; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('custom - %G'); return true; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('custom2 - %G'); return false; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('default - %G'); return true;
});
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('custom - %G'); return true;
});
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('custom2 - %G'); return false;
});
parse(parser2, INPUT);
assert.deepEqual(esc, ['custom2 - %G', 'custom - %G']);
});
it('Multiple custom handlers no fallback', () => {
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('default - %G'); return true; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('custom - %G'); return true; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('custom2 - %G'); return true; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('default - %G'); return true;
});
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('custom - %G'); return true;
});
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('custom2 - %G'); return true;
});
parse(parser2, INPUT);
assert.deepEqual(esc, ['custom2 - %G']);
});
it('Execution order should go from latest handler down to the original', () => {
const order: number[] = [];
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { order.push(1); return true; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { order.push(2); return false; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { order.push(3); return false; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
order.push(1); return true;
});
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
order.push(2); return false;
});
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
order.push(3); return false;
});
parse(parser2, '\x1b%G');
assert.deepEqual(order, [3, 2, 1]);
});
it('Dispose should work', () => {
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('default - %G'); return true; });
const dispo = parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('custom - %G'); return true; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('default - %G'); return true;
});
const dispo = parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('custom - %G'); return true;
});
dispo.dispose();
parse(parser2, INPUT);
assert.deepEqual(esc, ['default - %G']);
});
it('Should not corrupt the parser when dispose is called twice', () => {
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('default - %G'); return true; });
const dispo = parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => { esc.push('custom - %G'); return true; });
parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('default - %G'); return true;
});
const dispo = parser2.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
esc.push('custom - %G'); return true;
});
dispo.dispose();
dispo.dispose();
parse(parser2, INPUT);
@@ -1516,16 +1550,24 @@ describe('EscapeSequenceParser', () => {
describe('CSI custom handlers', () => {
it('Prevent fallback', () => {
const csiCustom: [string, ParamsArray, string][] = [];
parser2.registerCsiHandler({ final: 'm' }, params => { csi.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({ final: 'm' }, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({ final: 'm' }, params => {
csi.push(['m', params.toArray(), '']); return true;
});
parser2.registerCsiHandler({ final: 'm' }, params => {
csiCustom.push(['m', params.toArray(), '']); return true;
});
parse(parser2, INPUT);
assert.deepEqual(csi, [], 'Should not fallback to original handler');
assert.deepEqual(csiCustom, [['m', [1, 31], ''], ['m', [0], '']]);
});
it('Allow fallback', () => {
const csiCustom: [string, ParamsArray, string][] = [];
parser2.registerCsiHandler({ final: 'm' }, params => { csi.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({ final: 'm' }, params => { csiCustom.push(['m', params.toArray(), '']); return false; });
parser2.registerCsiHandler({ final: 'm' }, params => {
csi.push(['m', params.toArray(), '']); return true;
});
parser2.registerCsiHandler({ final: 'm' }, params => {
csiCustom.push(['m', params.toArray(), '']); return false;
});
parse(parser2, INPUT);
assert.deepEqual(csi, [['m', [1, 31], ''], ['m', [0], '']], 'Should fallback to original handler');
assert.deepEqual(csiCustom, [['m', [1, 31], ''], ['m', [0], '']]);
@@ -1533,9 +1575,15 @@ describe('EscapeSequenceParser', () => {
it('Multiple custom handlers fallback once', () => {
const csiCustom: [string, ParamsArray, string][] = [];
const csiCustom2: [string, ParamsArray, string][] = [];
parser2.registerCsiHandler({ final: 'm' }, params => { csi.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({ final: 'm' }, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({ final: 'm' }, params => { csiCustom2.push(['m', params.toArray(), '']); return false; });
parser2.registerCsiHandler({ final: 'm' }, params => {
csi.push(['m', params.toArray(), '']); return true;
});
parser2.registerCsiHandler({ final: 'm' }, params => {
csiCustom.push(['m', params.toArray(), '']); return true;
});
parser2.registerCsiHandler({ final: 'm' }, params => {
csiCustom2.push(['m', params.toArray(), '']); return false;
});
parse(parser2, INPUT);
assert.deepEqual(csi, [], 'Should not fallback to original handler');
assert.deepEqual(csiCustom, [['m', [1, 31], ''], ['m', [0], '']]);
@@ -1544,9 +1592,15 @@ describe('EscapeSequenceParser', () => {
it('Multiple custom handlers no fallback', () => {
const csiCustom: [string, ParamsArray, string][] = [];
const csiCustom2: [string, ParamsArray, string][] = [];
parser2.registerCsiHandler({ final: 'm' }, params => { csi.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({ final: 'm' }, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({ final: 'm' }, params => { csiCustom2.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({ final: 'm' }, params => {
csi.push(['m', params.toArray(), '']); return true;
});
parser2.registerCsiHandler({ final: 'm' }, params => {
csiCustom.push(['m', params.toArray(), '']); return true;
});
parser2.registerCsiHandler({ final: 'm' }, params => {
csiCustom2.push(['m', params.toArray(), '']); return true;
});
parse(parser2, INPUT);
assert.deepEqual(csi, [], 'Should not fallback to original handler');
assert.deepEqual(csiCustom, [], 'Should not fallback once');
@@ -1554,16 +1608,26 @@ describe('EscapeSequenceParser', () => {
});
it('Execution order should go from latest handler down to the original', () => {
const order: number[] = [];
parser2.registerCsiHandler({ final: 'm' }, () => { order.push(1); return true; });
parser2.registerCsiHandler({ final: 'm' }, () => { order.push(2); return false; });
parser2.registerCsiHandler({ final: 'm' }, () => { order.push(3); return false; });
parser2.registerCsiHandler({ final: 'm' }, () => {
order.push(1); return true;
});
parser2.registerCsiHandler({ final: 'm' }, () => {
order.push(2); return false;
});
parser2.registerCsiHandler({ final: 'm' }, () => {
order.push(3); return false;
});
parse(parser2, '\x1b[0m');
assert.deepEqual(order, [3, 2, 1]);
});
it('Dispose should work', () => {
const csiCustom: [string, ParamsArray, string][] = [];
parser2.registerCsiHandler({ final: 'm' }, params => { csi.push(['m', params.toArray(), '']); return true; });
const customHandler = parser2.registerCsiHandler({ final: 'm' }, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({ final: 'm' }, params => {
csi.push(['m', params.toArray(), '']); return true;
});
const customHandler = parser2.registerCsiHandler({ final: 'm' }, params => {
csiCustom.push(['m', params.toArray(), '']); return true;
});
customHandler.dispose();
parse(parser2, INPUT);
assert.deepEqual(csi, [['m', [1, 31], ''], ['m', [0], '']]);
@@ -1571,8 +1635,12 @@ describe('EscapeSequenceParser', () => {
});
it('Should not corrupt the parser when dispose is called twice', () => {
const csiCustom: [string, ParamsArray, string][] = [];
parser2.registerCsiHandler({ final: 'm' }, params => { csi.push(['m', params.toArray(), '']); return true; });
const customHandler = parser2.registerCsiHandler({ final: 'm' }, params => { csiCustom.push(['m', params.toArray(), '']); return true; });
parser2.registerCsiHandler({ final: 'm' }, params => {
csi.push(['m', params.toArray(), '']); return true;
});
const customHandler = parser2.registerCsiHandler({ final: 'm' }, params => {
csiCustom.push(['m', params.toArray(), '']); return true;
});
customHandler.dispose();
customHandler.dispose();
parse(parser2, INPUT);
@@ -1613,16 +1681,24 @@ describe('EscapeSequenceParser', () => {
describe('OSC custom handlers', () => {
it('Prevent fallback', () => {
const oscCustom: [number, string][] = [];
parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => {
osc.push([1, data]); return true;
}));
parser2.registerOscHandler(1, new OscHandler(data => {
oscCustom.push([1, data]); return true;
}));
parse(parser2, INPUT);
assert.deepEqual(osc, [], 'Should not fallback to original handler');
assert.deepEqual(oscCustom, [[1, 'foo=bar']]);
});
it('Allow fallback', () => {
const oscCustom: [number, string][] = [];
parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return false; }));
parser2.registerOscHandler(1, new OscHandler(data => {
osc.push([1, data]); return true;
}));
parser2.registerOscHandler(1, new OscHandler(data => {
oscCustom.push([1, data]); return false;
}));
parse(parser2, INPUT);
assert.deepEqual(osc, [[1, 'foo=bar']], 'Should fallback to original handler');
assert.deepEqual(oscCustom, [[1, 'foo=bar']]);
@@ -1630,9 +1706,15 @@ describe('EscapeSequenceParser', () => {
it('Multiple custom handlers fallback once', () => {
const oscCustom: [number, string][] = [];
const oscCustom2: [number, string][] = [];
parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { oscCustom2.push([1, data]); return false; }));
parser2.registerOscHandler(1, new OscHandler(data => {
osc.push([1, data]); return true;
}));
parser2.registerOscHandler(1, new OscHandler(data => {
oscCustom.push([1, data]); return true;
}));
parser2.registerOscHandler(1, new OscHandler(data => {
oscCustom2.push([1, data]); return false;
}));
parse(parser2, INPUT);
assert.deepEqual(osc, [], 'Should not fallback to original handler');
assert.deepEqual(oscCustom, [[1, 'foo=bar']]);
@@ -1641,9 +1723,15 @@ describe('EscapeSequenceParser', () => {
it('Multiple custom handlers no fallback', () => {
const oscCustom: [number, string][] = [];
const oscCustom2: [number, string][] = [];
parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => { oscCustom2.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => {
osc.push([1, data]); return true;
}));
parser2.registerOscHandler(1, new OscHandler(data => {
oscCustom.push([1, data]); return true;
}));
parser2.registerOscHandler(1, new OscHandler(data => {
oscCustom2.push([1, data]); return true;
}));
parse(parser2, INPUT);
assert.deepEqual(osc, [], 'Should not fallback to original handler');
assert.deepEqual(oscCustom, [], 'Should not fallback once');
@@ -1651,16 +1739,26 @@ describe('EscapeSequenceParser', () => {
});
it('Execution order should go from latest handler down to the original', () => {
const order: number[] = [];
parser2.registerOscHandler(1, new OscHandler(() => { order.push(1); return true; }));
parser2.registerOscHandler(1, new OscHandler(() => { order.push(2); return false; }));
parser2.registerOscHandler(1, new OscHandler(() => { order.push(3); return false; }));
parser2.registerOscHandler(1, new OscHandler(() => {
order.push(1); return true;
}));
parser2.registerOscHandler(1, new OscHandler(() => {
order.push(2); return false;
}));
parser2.registerOscHandler(1, new OscHandler(() => {
order.push(3); return false;
}));
parse(parser2, '\x1b]1;foo=bar\x1b\\');
assert.deepEqual(order, [3, 2, 1]);
});
it('Dispose should work', () => {
const oscCustom: [number, string][] = [];
parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; }));
const customHandler = parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => {
osc.push([1, data]); return true;
}));
const customHandler = parser2.registerOscHandler(1, new OscHandler(data => {
oscCustom.push([1, data]); return true;
}));
customHandler.dispose();
parse(parser2, INPUT);
assert.deepEqual(osc, [[1, 'foo=bar']]);
@@ -1668,8 +1766,12 @@ describe('EscapeSequenceParser', () => {
});
it('Should not corrupt the parser when dispose is called twice', () => {
const oscCustom: [number, string][] = [];
parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; }));
const customHandler = parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; }));
parser2.registerOscHandler(1, new OscHandler(data => {
osc.push([1, data]); return true;
}));
const customHandler = parser2.registerOscHandler(1, new OscHandler(data => {
oscCustom.push([1, data]); return true;
}));
customHandler.dispose();
customHandler.dispose();
parse(parser2, INPUT);
@@ -1712,54 +1814,88 @@ describe('EscapeSequenceParser', () => {
const DCS_INPUT = '\x1bP1;2;3+pabc\x1b\\';
it('Prevent fallback', () => {
const dcsCustom: [string, (number | number[])[], string][] = [];
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['A', params.toArray(), data]); return true;
}));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['B', params.toArray(), data]); return true;
}));
parse(parser2, DCS_INPUT);
assert.deepEqual(dcsCustom, [['B', [1, 2, 3], 'abc']]);
});
it('Allow fallback', () => {
const dcsCustom: [string, (number | number[])[], string][] = [];
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return false; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['A', params.toArray(), data]); return true;
}));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['B', params.toArray(), data]); return false;
}));
parse(parser2, DCS_INPUT);
assert.deepEqual(dcsCustom, [['B', [1, 2, 3], 'abc'], ['A', [1, 2, 3], 'abc']]);
});
it('Multiple custom handlers fallback once', () => {
const dcsCustom: [string, (number | number[])[], string][] = [];
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['C', params.toArray(), data]); return false; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['A', params.toArray(), data]); return true;
}));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['B', params.toArray(), data]); return true;
}));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['C', params.toArray(), data]); return false;
}));
parse(parser2, DCS_INPUT);
assert.deepEqual(dcsCustom, [['C', [1, 2, 3], 'abc'], ['B', [1, 2, 3], 'abc']]);
});
it('Multiple custom handlers no fallback', () => {
const dcsCustom: [string, (number | number[])[], string][] = [];
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['C', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['A', params.toArray(), data]); return true;
}));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['B', params.toArray(), data]); return true;
}));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['C', params.toArray(), data]); return true;
}));
parse(parser2, DCS_INPUT);
assert.deepEqual(dcsCustom, [['C', [1, 2, 3], 'abc']]);
});
it('Execution order should go from latest handler down to the original', () => {
const order: number[] = [];
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler(() => { order.push(1); return true; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler(() => { order.push(2); return false; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler(() => { order.push(3); return false; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler(() => {
order.push(1); return true;
}));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler(() => {
order.push(2); return false;
}));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler(() => {
order.push(3); return false;
}));
parse(parser2, DCS_INPUT);
assert.deepEqual(order, [3, 2, 1]);
});
it('Dispose should work', () => {
const dcsCustom: [string, (number | number[])[], string][] = [];
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; }));
const dispo = parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['A', params.toArray(), data]); return true;
}));
const dispo = parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['B', params.toArray(), data]); return true;
}));
dispo.dispose();
parse(parser2, DCS_INPUT);
assert.deepEqual(dcsCustom, [['A', [1, 2, 3], 'abc']]);
});
it('Should not corrupt the parser when dispose is called twice', () => {
const dcsCustom: [string, (number | number[])[], string][] = [];
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; }));
const dispo = parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; }));
parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['A', params.toArray(), data]); return true;
}));
const dispo = parser2.registerDcsHandler({ intermediates: '+', final: 'p' }, new DcsHandler((data, params) => {
dcsCustom.push(['B', params.toArray(), data]); return true;
}));
dispo.dispose();
dispo.dispose();
parse(parser2, DCS_INPUT);
@@ -1801,54 +1937,88 @@ describe('EscapeSequenceParser', () => {
const APC_INPUT = '\x1b_+pabc\x1b\\';
it('Prevent fallback', () => {
const apcCustom: [string, string][] = [];
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['A', data]); return true; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['B', data]); return true; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['A', data]); return true;
}));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['B', data]); return true;
}));
parse(parser2, APC_INPUT);
assert.deepEqual(apcCustom, [['B', 'abc']]);
});
it('Allow fallback', () => {
const apcCustom: [string, string][] = [];
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['A', data]); return true; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['B', data]); return false; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['A', data]); return true;
}));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['B', data]); return false;
}));
parse(parser2, APC_INPUT);
assert.deepEqual(apcCustom, [['B', 'abc'], ['A', 'abc']]);
});
it('Multiple custom handlers fallback once', () => {
const apcCustom: [string, string][] = [];
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['A', data]); return true; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['B', data]); return true; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['C', data]); return false; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['A', data]); return true;
}));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['B', data]); return true;
}));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['C', data]); return false;
}));
parse(parser2, APC_INPUT);
assert.deepEqual(apcCustom, [['C', 'abc'], ['B', 'abc']]);
});
it('Multiple custom handlers no fallback', () => {
const apcCustom: [string, string][] = [];
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['A', data]); return true; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['B', data]); return true; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['C', data]); return true; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['A', data]); return true;
}));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['B', data]); return true;
}));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['C', data]); return true;
}));
parse(parser2, APC_INPUT);
assert.deepEqual(apcCustom, [['C', 'abc']]);
});
it('Execution order should go from latest handler down to the original', () => {
const order: number[] = [];
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(() => { order.push(1); return true; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(() => { order.push(2); return false; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(() => { order.push(3); return false; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(() => {
order.push(1); return true;
}));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(() => {
order.push(2); return false;
}));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(() => {
order.push(3); return false;
}));
parse(parser2, APC_INPUT);
assert.deepEqual(order, [3, 2, 1]);
});
it('Dispose should work', () => {
const apcCustom: [string, string][] = [];
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['A', data]); return true; }));
const dispo = parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['B', data]); return true; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['A', data]); return true;
}));
const dispo = parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['B', data]); return true;
}));
dispo.dispose();
parse(parser2, APC_INPUT);
assert.deepEqual(apcCustom, [['A', 'abc']]);
});
it('Should not corrupt the parser when dispose is called twice', () => {
const apcCustom: [string, string][] = [];
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['A', data]); return true; }));
const dispo = parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => { apcCustom.push(['B', data]); return true; }));
parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['A', data]); return true;
}));
const dispo = parser2.registerApcHandler({ intermediates: '+', final: 'p' }, new ApcHandler(data => {
apcCustom.push(['B', data]); return true;
}));
dispo.dispose();
dispo.dispose();
parse(parser2, APC_INPUT);
@@ -1884,44 +2054,74 @@ describe('EscapeSequenceParser', () => {
const c = String.fromCharCode(i);
assert.equal(parser.identToString(parser.identifier({ prefix: c, final: 'z' })), c + 'z');
}
assert.throws(() => { parser.identifier({ prefix: '\x3b', final: 'z' }); }, 'prefix must be in range 0x3c .. 0x3f');
assert.throws(() => { parser.identifier({ prefix: '\x40', final: 'z' }); }, 'prefix must be in range 0x3c .. 0x3f');
assert.throws(() => { parser.identifier({ prefix: '??', final: 'z' }); }, 'only one byte as prefix supported');
assert.throws(() => {
parser.identifier({ prefix: '\x3b', final: 'z' });
}, 'prefix must be in range 0x3c .. 0x3f');
assert.throws(() => {
parser.identifier({ prefix: '\x40', final: 'z' });
}, 'prefix must be in range 0x3c .. 0x3f');
assert.throws(() => {
parser.identifier({ prefix: '??', final: 'z' });
}, 'only one byte as prefix supported');
});
it('intermediates range 0x20 .. 0x2f, up to two bytes', () => {
for (let i = 0x20; i <= 0x2f; ++i) {
const c = String.fromCharCode(i);
assert.equal(parser.identToString(parser.identifier({ intermediates: c + c, final: 'z' })), c + c + 'z');
}
assert.throws(() => { parser.identifier({ intermediates: '\x1f', final: 'z' }); }, 'intermediate must be in range 0x20 .. 0x2f');
assert.throws(() => { parser.identifier({ intermediates: '\x30', final: 'z' }); }, 'intermediate must be in range 0x20 .. 0x2f');
assert.throws(() => { parser.identifier({ intermediates: '!!!', final: 'z' }); }, 'only two bytes as intermediates are supported');
assert.throws(() => {
parser.identifier({ intermediates: '\x1f', final: 'z' });
}, 'intermediate must be in range 0x20 .. 0x2f');
assert.throws(() => {
parser.identifier({ intermediates: '\x30', final: 'z' });
}, 'intermediate must be in range 0x20 .. 0x2f');
assert.throws(() => {
parser.identifier({ intermediates: '!!!', final: 'z' });
}, 'only two bytes as intermediates are supported');
});
it('final CSI/DCS range 0x40 .. 0x7e (default), one byte', () => {
for (let i = 0x40; i <= 0x7e; ++i) {
const c = String.fromCharCode(i);
assert.equal(parser.identToString(parser.identifier({ final: c })), c);
}
assert.throws(() => { parser.identifier({ final: '\x3f' }); }, 'final must be in range 64 .. 126');
assert.throws(() => { parser.identifier({ final: '\x7f' }); }, 'final must be in range 64 .. 126');
assert.throws(() => { parser.identifier({ final: 'zz' }); }, 'final must be a single byte');
assert.throws(() => {
parser.identifier({ final: '\x3f' });
}, 'final must be in range 64 .. 126');
assert.throws(() => {
parser.identifier({ final: '\x7f' });
}, 'final must be in range 64 .. 126');
assert.throws(() => {
parser.identifier({ final: 'zz' });
}, 'final must be a single byte');
});
it('final ESC + APC range 0x30 .. 0x7e, one byte', () => {
for (let i = 0x30; i <= 0x7e; ++i) {
const final = String.fromCharCode(i);
let handler: IDisposable | undefined;
assert.doesNotThrow(() => { handler = parser.registerEscHandler({ final }, () => true); }, 'final must be in range 48 .. 126');
assert.doesNotThrow(() => {
handler = parser.registerEscHandler({ final }, () => true);
}, 'final must be in range 48 .. 126');
if (handler) handler.dispose();
assert.doesNotThrow(
() => { handler = parser.registerApcHandler({ final }, {start: () => {}, put: ()=>{}, end: ()=>true}); },
() => {
handler = parser.registerApcHandler({ final }, {start: () => {}, put: ()=>{}, end: ()=>true});
},
'final must be in range 48 .. 126'
);
if (handler) handler.dispose();
}
assert.throws(() => { parser.registerEscHandler({ final: '\x2f' }, () => true); }, 'final must be in range 48 .. 126');
assert.throws(() => { parser.registerEscHandler({ final: '\x7f' }, () => true); }, 'final must be in range 48 .. 126');
assert.throws(() => { parser.registerApcHandler({ final: '\x2f' }, {start: () => {}, put: ()=>{}, end: ()=>true}); }, 'final must be in range 48 .. 126');
assert.throws(() => { parser.registerApcHandler({ final: '\x7f' }, {start: () => {}, put: ()=>{}, end: ()=>true}); }, 'final must be in range 48 .. 126');
assert.throws(() => {
parser.registerEscHandler({ final: '\x2f' }, () => true);
}, 'final must be in range 48 .. 126');
assert.throws(() => {
parser.registerEscHandler({ final: '\x7f' }, () => true);
}, 'final must be in range 48 .. 126');
assert.throws(() => {
parser.registerApcHandler({ final: '\x2f' }, {start: () => {}, put: ()=>{}, end: ()=>true});
}, 'final must be in range 48 .. 126');
assert.throws(() => {
parser.registerApcHandler({ final: '\x7f' }, {start: () => {}, put: ()=>{}, end: ()=>true});
}, 'final must be in range 48 .. 126');
});
it('id calculation - should stacking prefix -> intermediate -> final', () => {
assert.equal(parser.identToString(parser.identifier({ final: 'z' })), 'z');
@@ -1934,9 +2134,15 @@ describe('EscapeSequenceParser', () => {
describe('identifier invocation', () => {
it('ESC', () => {
const callstack: string[] = [];
const h1 = parser.registerEscHandler({ final: 'z' }, () => { callstack.push('z'); return true; });
const h2 = parser.registerEscHandler({ intermediates: '!', final: 'z' }, () => { callstack.push('!z'); return true; });
const h3 = parser.registerEscHandler({ intermediates: '!!', final: 'z' }, () => { callstack.push('!!z'); return true; });
const h1 = parser.registerEscHandler({ final: 'z' }, () => {
callstack.push('z'); return true;
});
const h2 = parser.registerEscHandler({ intermediates: '!', final: 'z' }, () => {
callstack.push('!z'); return true;
});
const h3 = parser.registerEscHandler({ intermediates: '!!', final: 'z' }, () => {
callstack.push('!!z'); return true;
});
parse(parser, '\x1bz\x1b!z\x1b!!z');
h1.dispose();
h2.dispose();
@@ -1946,12 +2152,24 @@ describe('EscapeSequenceParser', () => {
});
it('CSI', () => {
const callstack: any[] = [];
const h1 = parser.registerCsiHandler({ final: 'z' }, params => { callstack.push(['z', params.toArray()]); return true; });
const h2 = parser.registerCsiHandler({ intermediates: '!', final: 'z' }, params => { callstack.push(['!z', params.toArray()]); return true; });
const h3 = parser.registerCsiHandler({ intermediates: '!!', final: 'z' }, params => { callstack.push(['!!z', params.toArray()]); return true; });
const h4 = parser.registerCsiHandler({ prefix: '?', final: 'z' }, params => { callstack.push(['?z', params.toArray()]); return true; });
const h5 = parser.registerCsiHandler({ prefix: '?', intermediates: '!', final: 'z' }, params => { callstack.push(['?!z', params.toArray()]); return true; });
const h6 = parser.registerCsiHandler({ prefix: '?', intermediates: '!!', final: 'z' }, params => { callstack.push(['?!!z', params.toArray()]); return true; });
const h1 = parser.registerCsiHandler({ final: 'z' }, params => {
callstack.push(['z', params.toArray()]); return true;
});
const h2 = parser.registerCsiHandler({ intermediates: '!', final: 'z' }, params => {
callstack.push(['!z', params.toArray()]); return true;
});
const h3 = parser.registerCsiHandler({ intermediates: '!!', final: 'z' }, params => {
callstack.push(['!!z', params.toArray()]); return true;
});
const h4 = parser.registerCsiHandler({ prefix: '?', final: 'z' }, params => {
callstack.push(['?z', params.toArray()]); return true;
});
const h5 = parser.registerCsiHandler({ prefix: '?', intermediates: '!', final: 'z' }, params => {
callstack.push(['?!z', params.toArray()]); return true;
});
const h6 = parser.registerCsiHandler({ prefix: '?', intermediates: '!!', final: 'z' }, params => {
callstack.push(['?!!z', params.toArray()]); return true;
});
parse(parser, '\x1b[1;z\x1b[1;!z\x1b[1;!!z\x1b[?1;z\x1b[?1;!z\x1b[?1;!!z');
h1.dispose();
h2.dispose();
@@ -1967,12 +2185,24 @@ describe('EscapeSequenceParser', () => {
});
it('DCS', () => {
const callstack: any[] = [];
const h1 = parser.registerDcsHandler({ final: 'z' }, new DcsHandler((data, params) => { callstack.push(['z', params.toArray(), data]); return true; }));
const h2 = parser.registerDcsHandler({ intermediates: '!', final: 'z' }, new DcsHandler((data, params) => { callstack.push(['!z', params.toArray(), data]); return true; }));
const h3 = parser.registerDcsHandler({ intermediates: '!!', final: 'z' }, new DcsHandler((data, params) => { callstack.push(['!!z', params.toArray(), data]); return true; }));
const h4 = parser.registerDcsHandler({ prefix: '?', final: 'z' }, new DcsHandler((data, params) => { callstack.push(['?z', params.toArray(), data]); return true; }));
const h5 = parser.registerDcsHandler({ prefix: '?', intermediates: '!', final: 'z' }, new DcsHandler((data, params) => { callstack.push(['?!z', params.toArray(), data]); return true; }));
const h6 = parser.registerDcsHandler({ prefix: '?', intermediates: '!!', final: 'z' }, new DcsHandler((data, params) => { callstack.push(['?!!z', params.toArray(), data]); return true; }));
const h1 = parser.registerDcsHandler({ final: 'z' }, new DcsHandler((data, params) => {
callstack.push(['z', params.toArray(), data]); return true;
}));
const h2 = parser.registerDcsHandler({ intermediates: '!', final: 'z' }, new DcsHandler((data, params) => {
callstack.push(['!z', params.toArray(), data]); return true;
}));
const h3 = parser.registerDcsHandler({ intermediates: '!!', final: 'z' }, new DcsHandler((data, params) => {
callstack.push(['!!z', params.toArray(), data]); return true;
}));
const h4 = parser.registerDcsHandler({ prefix: '?', final: 'z' }, new DcsHandler((data, params) => {
callstack.push(['?z', params.toArray(), data]); return true;
}));
const h5 = parser.registerDcsHandler({ prefix: '?', intermediates: '!', final: 'z' }, new DcsHandler((data, params) => {
callstack.push(['?!z', params.toArray(), data]); return true;
}));
const h6 = parser.registerDcsHandler({ prefix: '?', intermediates: '!!', final: 'z' }, new DcsHandler((data, params) => {
callstack.push(['?!!z', params.toArray(), data]); return true;
}));
parse(parser, '\x1bP1;zAB\x1b\\\x1bP1;!zAB\x1b\\\x1bP1;!!zAB\x1b\\\x1bP?1;zAB\x1b\\\x1bP?1;!zAB\x1b\\\x1bP?1;!!zAB\x1b\\');
h1.dispose();
h2.dispose();
@@ -1995,9 +2225,15 @@ describe('EscapeSequenceParser', () => {
});
it('APC', () => {
const callstack: any[] = [];
const h1 = parser.registerApcHandler({ final: 'z' }, new ApcHandler(data => { callstack.push(['z', data]); return true; }));
const h2 = parser.registerApcHandler({ intermediates: '!', final: 'z' }, new ApcHandler(data => { callstack.push(['!z', data]); return true; }));
const h3 = parser.registerApcHandler({ intermediates: '!!', final: 'z' }, new ApcHandler(data => { callstack.push(['!!z', data]); return true; }));
const h1 = parser.registerApcHandler({ final: 'z' }, new ApcHandler(data => {
callstack.push(['z', data]); return true;
}));
const h2 = parser.registerApcHandler({ intermediates: '!', final: 'z' }, new ApcHandler(data => {
callstack.push(['!z', data]); return true;
}));
const h3 = parser.registerApcHandler({ intermediates: '!!', final: 'z' }, new ApcHandler(data => {
callstack.push(['!!z', data]); return true;
}));
parse(parser, '\x1b_zAB\x1b\\\x1b_!zAB\x1b\\\x1b_!!zAB\x1b\\');
h1.dispose();
h2.dispose();
@@ -2106,14 +2342,30 @@ describe('EscapeSequenceParser - async', () => {
}
callstack.push(['PRINT', result]);
});
parser.registerCsiHandler({ final: 'm' }, params => { callstack.push(['SGR', params.toArray()]); return true; });
parser.registerEscHandler({ intermediates: '%', final: 'G' }, () => { callstack.push(['ESC %G']); return true; });
parser.registerEscHandler({ final: 'E' }, () => { callstack.push(['ESC E']); return true; });
parser.setExecuteHandler('\r', () => { callstack.push(['EXE \r']); return true; });
parser.setExecuteHandler('\n', () => { callstack.push(['EXE \n']); return true; });
parser.registerOscHandler(1, new OscHandler(data => { callstack.push(['OSC 1', data]); return true; }));
parser.registerDcsHandler({ final: 'a' }, new DcsHandler((data, params) => { callstack.push(['DCS a', [data, params.toArray()]]); return true; }));
parser.registerApcHandler({ final: 'X' }, new ApcHandler(data => { callstack.push(['APC X', data]); return true; }));
parser.registerCsiHandler({ final: 'm' }, params => {
callstack.push(['SGR', params.toArray()]); return true;
});
parser.registerEscHandler({ intermediates: '%', final: 'G' }, () => {
callstack.push(['ESC %G']); return true;
});
parser.registerEscHandler({ final: 'E' }, () => {
callstack.push(['ESC E']); return true;
});
parser.setExecuteHandler('\r', () => {
callstack.push(['EXE \r']); return true;
});
parser.setExecuteHandler('\n', () => {
callstack.push(['EXE \n']); return true;
});
parser.registerOscHandler(1, new OscHandler(data => {
callstack.push(['OSC 1', data]); return true;
}));
parser.registerDcsHandler({ final: 'a' }, new DcsHandler((data, params) => {
callstack.push(['DCS a', [data, params.toArray()]]); return true;
}));
parser.registerApcHandler({ final: 'X' }, new ApcHandler(data => {
callstack.push(['APC X', data]); return true;
}));
});
it('sync handlers keep being parsed in sync mode', () => {
@@ -2142,14 +2394,30 @@ describe('EscapeSequenceParser - async', () => {
}
callstack.push(['PRINT', result]);
});
parser.registerCsiHandler({ final: 'm' }, async params => { callstack.push(['SGR', params.toArray()]); return true; });
parser.registerEscHandler({ intermediates: '%', final: 'G' }, async () => { callstack.push(['ESC %G']); return true; });
parser.registerEscHandler({ final: 'E' }, async () => { callstack.push(['ESC E']); return true; });
parser.setExecuteHandler('\r', () => { callstack.push(['EXE \r']); return true; });
parser.setExecuteHandler('\n', () => { callstack.push(['EXE \n']); return true; });
parser.registerOscHandler(1, new OscHandler(async data => { callstack.push(['OSC 1', data]); return true; }));
parser.registerDcsHandler({ final: 'a' }, new DcsHandler(async (data, params) => { callstack.push(['DCS a', [data, params.toArray()]]); return true; }));
parser.registerApcHandler({ final: 'X' }, new ApcHandler(async data => { callstack.push(['APC X', data]); return true; }));
parser.registerCsiHandler({ final: 'm' }, async params => {
callstack.push(['SGR', params.toArray()]); return true;
});
parser.registerEscHandler({ intermediates: '%', final: 'G' }, async () => {
callstack.push(['ESC %G']); return true;
});
parser.registerEscHandler({ final: 'E' }, async () => {
callstack.push(['ESC E']); return true;
});
parser.setExecuteHandler('\r', () => {
callstack.push(['EXE \r']); return true;
});
parser.setExecuteHandler('\n', () => {
callstack.push(['EXE \n']); return true;
});
parser.registerOscHandler(1, new OscHandler(async data => {
callstack.push(['OSC 1', data]); return true;
}));
parser.registerDcsHandler({ final: 'a' }, new DcsHandler(async (data, params) => {
callstack.push(['DCS a', [data, params.toArray()]]); return true;
}));
parser.registerApcHandler({ final: 'X' }, new ApcHandler(async data => {
callstack.push(['APC X', data]); return true;
}));
});
it('sync parse call does not work anymore', () => {
@@ -2239,7 +2507,9 @@ describe('EscapeSequenceParser - async', () => {
});
it('multiple async SGR handlers', async () => {
// register with fallback
const SGR2 = parser.registerCsiHandler({ final: 'm' }, async params => { callstack.push(['2# SGR', params.toArray()]); return false; });
const SGR2 = parser.registerCsiHandler({ final: 'm' }, async params => {
callstack.push(['2# SGR', params.toArray()]); return false;
});
await parseP(parser, INPUT);
// should contain [2# SGR, SGR] call pairs
for (let i = 0; i < callstack.length; ++i) {
@@ -2274,7 +2544,9 @@ describe('EscapeSequenceParser - async', () => {
clearAccu();
// register without fallback
const SGR22 = parser.registerCsiHandler({ final: 'm' }, async params => { callstack.push(['2# SGR', params.toArray()]); return true; });
const SGR22 = parser.registerCsiHandler({ final: 'm' }, async params => {
callstack.push(['2# SGR', params.toArray()]); return true;
});
await parseP(parser, INPUT);
// should only contain 2# SGR
for (let i = 0; i < callstack.length; ++i) {
@@ -2307,7 +2579,9 @@ describe('EscapeSequenceParser - async', () => {
});
it('multiple async ESC handlers', async () => {
// register with fallback
const ESC2 = parser.registerEscHandler({ final: 'E' }, async () => { callstack.push(['2# ESC E']); return false; });
const ESC2 = parser.registerEscHandler({ final: 'E' }, async () => {
callstack.push(['2# ESC E']); return false;
});
await parseP(parser, INPUT);
for (let i = 0; i < callstack.length; ++i) {
const entry = callstack[i];
@@ -2340,7 +2614,9 @@ describe('EscapeSequenceParser - async', () => {
clearAccu();
// register without fallback
const ESC22 = parser.registerEscHandler({ final: 'E' }, async () => { callstack.push(['2# ESC E']); return true; });
const ESC22 = parser.registerEscHandler({ final: 'E' }, async () => {
callstack.push(['2# ESC E']); return true;
});
await parseP(parser, INPUT);
for (let i = 0; i < callstack.length; ++i) {
const entry = callstack[i];
@@ -2372,9 +2648,13 @@ describe('EscapeSequenceParser - async', () => {
});
it('sync/async SGR mixed', async () => {
// sync with fallback
const SGR2 = parser.registerCsiHandler({ final: 'm' }, params => { callstack.push(['2# SGR', params.toArray()]); return false; });
const SGR2 = parser.registerCsiHandler({ final: 'm' }, params => {
callstack.push(['2# SGR', params.toArray()]); return false;
});
// async with fallback
const SGR3 = parser.registerCsiHandler({ final: 'm' }, async params => { callstack.push(['3# SGR', params.toArray()]); return false; });
const SGR3 = parser.registerCsiHandler({ final: 'm' }, async params => {
callstack.push(['3# SGR', params.toArray()]); return false;
});
await parseP(parser, INPUT);
// should contain [3# SGR, 2# SGR, SGR] call triples
for (let i = 0; i < callstack.length; ++i) {
@@ -2434,7 +2714,9 @@ describe('EscapeSequenceParser - async', () => {
});
it('multiple async OSC handlers', async () => {
// register with fallback
const OSC2 = parser.registerOscHandler(1, new OscHandler(async data => { callstack.push(['2# OSC 1', data]); return false; }));
const OSC2 = parser.registerOscHandler(1, new OscHandler(async data => {
callstack.push(['2# OSC 1', data]); return false;
}));
await parseP(parser, INPUT);
for (let i = 0; i < callstack.length; ++i) {
const entry = callstack[i];
@@ -2467,7 +2749,9 @@ describe('EscapeSequenceParser - async', () => {
clearAccu();
// register without fallback
const OSC22 = parser.registerOscHandler(1, new OscHandler(async data => { callstack.push(['2# OSC 1', data]); return true; }));
const OSC22 = parser.registerOscHandler(1, new OscHandler(async data => {
callstack.push(['2# OSC 1', data]); return true;
}));
await parseP(parser, INPUT);
for (let i = 0; i < callstack.length; ++i) {
const entry = callstack[i];
@@ -2500,7 +2784,9 @@ describe('EscapeSequenceParser - async', () => {
});
it('multiple async DCS handlers', async () => {
// register with fallback
const DCS2 = parser.registerDcsHandler({ final: 'a' }, new DcsHandler(async (data, params) => { callstack.push(['#2 DCS a', [data, params.toArray()]]); return false; }));
const DCS2 = parser.registerDcsHandler({ final: 'a' }, new DcsHandler(async (data, params) => {
callstack.push(['#2 DCS a', [data, params.toArray()]]); return false;
}));
await parseP(parser, INPUT);
for (let i = 0; i < callstack.length; ++i) {
const entry = callstack[i];
@@ -2533,7 +2819,9 @@ describe('EscapeSequenceParser - async', () => {
clearAccu();
// register without fallback
const DCS22 = parser.registerDcsHandler({ final: 'a' }, new DcsHandler(async (data, params) => { callstack.push(['#2 DCS a', [data, params.toArray()]]); return true; }));
const DCS22 = parser.registerDcsHandler({ final: 'a' }, new DcsHandler(async (data, params) => {
callstack.push(['#2 DCS a', [data, params.toArray()]]); return true;
}));
await parseP(parser, INPUT);
for (let i = 0; i < callstack.length; ++i) {
const entry = callstack[i];
@@ -2566,7 +2854,9 @@ describe('EscapeSequenceParser - async', () => {
});
it('multiple async APC handlers', async () => {
// register with fallback
const APC2 = parser.registerApcHandler({ final: 'X' }, new ApcHandler(async data => { callstack.push(['#2 APC X', data]); return false; }));
const APC2 = parser.registerApcHandler({ final: 'X' }, new ApcHandler(async data => {
callstack.push(['#2 APC X', data]); return false;
}));
await parseP(parser, INPUT);
for (let i = 0; i < callstack.length; ++i) {
const entry = callstack[i];
@@ -2599,7 +2889,9 @@ describe('EscapeSequenceParser - async', () => {
clearAccu();
// register without fallback
const APC22 = parser.registerApcHandler({ final: 'X' }, new ApcHandler(async data => { callstack.push(['#2 APC X', data]); return true; }));
const APC22 = parser.registerApcHandler({ final: 'X' }, new ApcHandler(async data => {
callstack.push(['#2 APC X', data]); return true;
}));
await parseP(parser, INPUT);
for (let i = 0; i < callstack.length; ++i) {
const entry = callstack[i];
+42 -14
View File
@@ -185,7 +185,9 @@ describe('OscParser', () => {
});
it('should be called once on end(true)', () => {
parser.registerHandler(1234, new OscHandler(data => { reports.push([1234, data]); return true; }));
parser.registerHandler(1234, new OscHandler(data => {
reports.push([1234, data]); return true;
}));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -195,7 +197,9 @@ describe('OscParser', () => {
assert.deepEqual(reports, [[1234, 'Here comes the mouse!']]);
});
it('should not be called on end(false)', () => {
parser.registerHandler(1234, new OscHandler(data => { reports.push([1234, data]); return true; }));
parser.registerHandler(1234, new OscHandler(data => {
reports.push([1234, data]); return true;
}));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -205,8 +209,12 @@ describe('OscParser', () => {
assert.deepEqual(reports, []);
});
it('should be disposable', () => {
parser.registerHandler(1234, new OscHandler(data => { reports.push(['one', data]); return true; }));
const dispo = parser.registerHandler(1234, new OscHandler(data => { reports.push(['two', data]); return true; }));
parser.registerHandler(1234, new OscHandler(data => {
reports.push(['one', data]); return true;
}));
const dispo = parser.registerHandler(1234, new OscHandler(data => {
reports.push(['two', data]); return true;
}));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -224,8 +232,12 @@ describe('OscParser', () => {
assert.deepEqual(reports, [['two', 'Here comes the mouse!'], ['one', 'some other data']]);
});
it('should respect return false', () => {
parser.registerHandler(1234, new OscHandler(data => { reports.push(['one', data]); return true; }));
parser.registerHandler(1234, new OscHandler(data => { reports.push(['two', data]); return false; }));
parser.registerHandler(1234, new OscHandler(data => {
reports.push(['one', data]); return true;
}));
parser.registerHandler(1234, new OscHandler(data => {
reports.push(['two', data]); return false;
}));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -236,7 +248,9 @@ describe('OscParser', () => {
});
it('should work up to payload limit', function(): void {
this.timeout(30000);
parser.registerHandler(1234, new OscHandler(data => { reports.push([1234, data]); return true; }));
parser.registerHandler(1234, new OscHandler(data => {
reports.push([1234, data]); return true;
}));
parser.start();
let data = toUtf32('1234;');
parser.put(data, 0, data.length);
@@ -249,7 +263,9 @@ describe('OscParser', () => {
});
it('should abort for payload limit +1', function(): void {
this.timeout(30000);
parser.registerHandler(1234, new OscHandler(data => { reports.push([1234, data]); return true; }));
parser.registerHandler(1234, new OscHandler(data => {
reports.push([1234, data]); return true;
}));
parser.start();
let data = toUtf32('1234;');
parser.put(data, 0, data.length);
@@ -412,7 +428,9 @@ describe('OscParser - async tests', () => {
});
describe('OscHandlerFactory', () => {
it('should be called once on end(true)', async () => {
parser.registerHandler(1234, new OscHandler(async data => { reports.push([1234, data]); return true; }));
parser.registerHandler(1234, new OscHandler(async data => {
reports.push([1234, data]); return true;
}));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -423,7 +441,9 @@ describe('OscParser - async tests', () => {
assert.deepEqual(reports, [[1234, 'Here comes the mouse!']]);
});
it('should not be called on end(false)', async () => {
parser.registerHandler(1234, new OscHandler(async data => { reports.push([1234, data]); return true; }));
parser.registerHandler(1234, new OscHandler(async data => {
reports.push([1234, data]); return true;
}));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -433,8 +453,12 @@ describe('OscParser - async tests', () => {
assert.deepEqual(reports, []);
});
it('should be disposable', async () => {
parser.registerHandler(1234, new OscHandler(async data => { reports.push(['one', data]); return true; }));
const dispo = parser.registerHandler(1234, new OscHandler(async data => { reports.push(['two', data]); return true; }));
parser.registerHandler(1234, new OscHandler(async data => {
reports.push(['one', data]); return true;
}));
const dispo = parser.registerHandler(1234, new OscHandler(async data => {
reports.push(['two', data]); return true;
}));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
@@ -452,8 +476,12 @@ describe('OscParser - async tests', () => {
assert.deepEqual(reports, [['two', 'Here comes the mouse!'], ['one', 'some other data']]);
});
it('should respect return false', async () => {
parser.registerHandler(1234, new OscHandler(async data => { reports.push(['one', data]); return true; }));
parser.registerHandler(1234, new OscHandler(async data => { reports.push(['two', data]); return false; }));
parser.registerHandler(1234, new OscHandler(async data => {
reports.push(['one', data]); return true;
}));
parser.registerHandler(1234, new OscHandler(async data => {
reports.push(['two', data]); return false;
}));
parser.start();
let data = toUtf32('1234;Here comes');
parser.put(data, 0, data.length);
+3 -1
View File
@@ -40,7 +40,9 @@ describe('AddonManager', () => {
let called = 0;
class Addon implements ITerminalAddon {
public activate(): void {}
public dispose(): void { called++; }
public dispose(): void {
called++;
}
}
manager.loadAddon(null!, new Addon());
manager.loadAddon(null!, new Addon());
+18 -6
View File
@@ -19,11 +19,21 @@ export class BufferApiView implements IBufferApi {
return this;
}
public get cursorY(): number { return this._buffer.y; }
public get cursorX(): number { return this._buffer.x; }
public get viewportY(): number { return this._buffer.ydisp; }
public get baseY(): number { return this._buffer.ybase; }
public get length(): number { return this._buffer.lines.length; }
public get cursorY(): number {
return this._buffer.y;
}
public get cursorX(): number {
return this._buffer.x;
}
public get viewportY(): number {
return this._buffer.ydisp;
}
public get baseY(): number {
return this._buffer.ybase;
}
public get length(): number {
return this._buffer.lines.length;
}
public getLine(y: number): IBufferLineApi | undefined {
const line = this._buffer.lines.get(y);
if (!line) {
@@ -31,5 +41,7 @@ export class BufferApiView implements IBufferApi {
}
return new BufferLineApiView(line);
}
public getNullCell(): IBufferCellApi { return new CellData(); }
public getNullCell(): IBufferCellApi {
return new CellData();
}
}
+6 -2
View File
@@ -10,8 +10,12 @@ import { IBufferCell as IBufferCellApi, IBufferLine as IBufferLineApi } from '@x
export class BufferLineApiView implements IBufferLineApi {
constructor(private _line: IBufferLine) { }
public get isWrapped(): boolean { return this._line.isWrapped; }
public get length(): number { return this._line.length; }
public get isWrapped(): boolean {
return this._line.isWrapped;
}
public get length(): number {
return this._line.length;
}
public getCell(x: number, cell?: IBufferCellApi): IBufferCellApi | undefined {
if (x < 0 || x >= this._line.length) {
return undefined;
+6 -2
View File
@@ -23,8 +23,12 @@ export class BufferNamespaceApi extends Disposable implements IBufferNamespaceAp
this._core.buffers.onBufferActivate(() => this._onBufferChange.fire(this.active));
}
public get active(): IBufferApi {
if (this._core.buffers.active === this._core.buffers.normal) { return this.normal; }
if (this._core.buffers.active === this._core.buffers.alt) { return this.alternate; }
if (this._core.buffers.active === this._core.buffers.normal) {
return this.normal;
}
if (this._core.buffers.active === this._core.buffers.alt) {
return this.alternate;
}
throw new Error('Active buffer is neither normal nor alternate');
}
public get normal(): IBufferApi {
+3 -1
View File
@@ -29,7 +29,9 @@ export class BufferService extends Disposable implements IBufferService {
private readonly _onScroll = this._register(new Emitter<number>());
public readonly onScroll = this._onScroll.event;
public get buffer(): IBuffer { return this.buffers.active; }
public get buffer(): IBuffer {
return this.buffers.active;
}
/** An IBufferline to clone/copy from for new blank lines */
private _cachedBlankLine: IBufferLine | undefined;
+3 -1
View File
@@ -32,7 +32,9 @@ export class DecorationService extends Disposable implements IDecorationService
private readonly _onDecorationRemoved = this._register(new Emitter<IInternalDecoration>());
public readonly onDecorationRemoved = this._onDecorationRemoved.event;
public get decorations(): IterableIterator<IInternalDecoration> { return this._decorations.values(); }
public get decorations(): IterableIterator<IInternalDecoration> {
return this._decorations.values();
}
constructor(@ILogService private readonly _logService: ILogService) {
super();
+3 -1
View File
@@ -35,7 +35,9 @@ export class LogService extends Disposable implements ILogService {
public serviceBrand: any;
private _logLevel: LogLevelEnum = LogLevelEnum.OFF;
public get logLevel(): LogLevelEnum { return this._logLevel; }
public get logLevel(): LogLevelEnum {
return this._logLevel;
}
constructor(
@IOptionsService private readonly _optionsService: IOptionsService
@@ -40,8 +40,12 @@ describe('MouseStateService', () => {
cms.reset();
assert.equal(cms.activeEncoding, 'DEFAULT');
assert.equal(cms.activeProtocol, 'NONE');
assert.throws(() => { cms.activeEncoding = 'xyz'; }, 'unknown encoding "xyz"');
assert.throws(() => { cms.activeProtocol = 'xyz'; }, 'unknown protocol "xyz"');
assert.throws(() => {
cms.activeEncoding = 'xyz';
}, 'unknown encoding "xyz"');
assert.throws(() => {
cms.activeProtocol = 'xyz';
}, 'unknown protocol "xyz"');
});
it('addEncoding', () => {
const cms = new MouseStateService();
+6 -2
View File
@@ -25,11 +25,15 @@ describe('unicode provider', () => {
it('default to V6', () => {
assert.equal(us.activeVersion, '6');
assert.deepEqual(us.versions, ['6']);
assert.doesNotThrow(() => { us.activeVersion = '6'; }, `unknown Unicode version "6"`);
assert.doesNotThrow(() => {
us.activeVersion = '6';
}, `unknown Unicode version "6"`);
assert.equal(us.getStringCellWidth('hello'), 5);
});
it('activate should throw for unknown version', () => {
assert.throws(() => { us.activeVersion = '55'; }, 'unknown Unicode version "55"');
assert.throws(() => {
us.activeVersion = '55';
}, 'unknown Unicode version "55"');
});
it('should notify about version change', () => {
const notes: string[] = [];
+18 -6
View File
@@ -35,8 +35,12 @@ describe('Headless API Tests', function (): void {
it('write with callback', async () => {
let result: string | undefined;
await new Promise<void>(r => {
term.write('foo', () => { result = 'a'; });
term.write('bar', () => { result += 'b'; });
term.write('foo', () => {
result = 'a';
});
term.write('bar', () => {
result += 'b';
});
term.write('文', () => {
result += 'c';
r();
@@ -56,8 +60,12 @@ describe('Headless API Tests', function (): void {
it('write - bytes (UTF8) with callback', async () => {
let result: string | undefined;
await new Promise<void>(r => {
term.write(new Uint8Array([102, 111, 111]), () => { result = 'A'; }); // foo
term.write(new Uint8Array([98, 97, 114]), () => { result += 'B'; }); // bar
term.write(new Uint8Array([102, 111, 111]), () => {
result = 'A';
}); // foo
term.write(new Uint8Array([98, 97, 114]), () => {
result += 'B';
}); // bar
term.write(new Uint8Array([230, 150, 135]), () => { // 文
result += 'C';
r();
@@ -79,8 +87,12 @@ describe('Headless API Tests', function (): void {
it('writeln with callback', async () => {
let result: string | undefined;
await new Promise<void>(r => {
term.writeln('foo', () => { result = '1'; });
term.writeln('bar', () => { result += '2'; });
term.writeln('foo', () => {
result = '1';
});
term.writeln('bar', () => {
result += '2';
});
term.writeln('文', () => {
result += '3';
r();
+36 -12
View File
@@ -72,16 +72,36 @@ export class Terminal extends Disposable implements ITerminalApi {
}
}
public get onBell(): IEvent<void> { return this._core.onBell; }
public get onBinary(): IEvent<string> { return this._core.onBinary; }
public get onCursorMove(): IEvent<void> { return this._core.onCursorMove; }
public get onData(): IEvent<string> { return this._core.onData; }
public get onLineFeed(): IEvent<void> { return this._core.onLineFeed; }
public get onRender(): IEvent<{ start: number, end: number }> { return this._core.onRender; }
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; }
public get onScroll(): IEvent<number> { return this._core.onScroll; }
public get onTitleChange(): IEvent<string> { return this._core.onTitleChange; }
public get onWriteParsed(): IEvent<void> { return this._core.onWriteParsed; }
public get onBell(): IEvent<void> {
return this._core.onBell;
}
public get onBinary(): IEvent<string> {
return this._core.onBinary;
}
public get onCursorMove(): IEvent<void> {
return this._core.onCursorMove;
}
public get onData(): IEvent<string> {
return this._core.onData;
}
public get onLineFeed(): IEvent<void> {
return this._core.onLineFeed;
}
public get onRender(): IEvent<{ start: number, end: number }> {
return this._core.onRender;
}
public get onResize(): IEvent<{ cols: number, rows: number }> {
return this._core.onResize;
}
public get onScroll(): IEvent<number> {
return this._core.onScroll;
}
public get onTitleChange(): IEvent<string> {
return this._core.onTitleChange;
}
public get onWriteParsed(): IEvent<void> {
return this._core.onWriteParsed;
}
public get parser(): IParser {
this._parser ??= new ParserApi(this._core);
@@ -91,8 +111,12 @@ export class Terminal extends Disposable implements ITerminalApi {
this._checkProposedApi();
return new UnicodeApi(this._core);
}
public get rows(): number { return this._core.rows; }
public get cols(): number { return this._core.cols; }
public get rows(): number {
return this._core.rows;
}
public get cols(): number {
return this._core.cols;
}
public get buffer(): IBufferNamespaceApi {
this._buffer ??= this._register(new BufferNamespaceApi(this._core));
return this._buffer;
@@ -24,19 +24,25 @@ function toUtf32(s: string): Uint32Array {
class FastDcsHandler implements IDcsHandler {
public hook(params: IParams): void {}
public put(data: Uint32Array, start: number, end: number): void {}
public unhook(success: boolean): boolean { return true; }
public unhook(success: boolean): boolean {
return true;
}
}
class FastOscHandler implements IOscHandler {
public start(): void {}
public put(data: Uint32Array, start: number, end: number): void {}
public end(success: boolean): boolean { return true; }
public end(success: boolean): boolean {
return true;
}
}
class FastApcHandler implements IApcHandler {
public start(): void {}
public put(data: Uint32Array, start: number, end: number): void {}
public end(success: boolean): boolean { return true; }
public end(success: boolean): boolean {
return true;
}
}
+12 -4
View File
@@ -27,7 +27,9 @@ perfContext('Emitter.fire()', () => {
let sum = 0;
before(() => {
emitter = new Emitter<number>();
emitter.event(e => { sum += e; });
emitter.event(e => {
sum += e;
});
});
new RuntimeCase('', () => {
for (let i = 0; i < ITERATIONS; i++) {
@@ -42,8 +44,12 @@ perfContext('Emitter.fire()', () => {
let sum = 0;
before(() => {
emitter = new Emitter<number>();
emitter.event(e => { sum += e; });
emitter.event(e => { sum += e * 2; });
emitter.event(e => {
sum += e;
});
emitter.event(e => {
sum += e * 2;
});
});
new RuntimeCase('', () => {
for (let i = 0; i < ITERATIONS; i++) {
@@ -59,7 +65,9 @@ perfContext('Emitter.fire()', () => {
before(() => {
emitter = new Emitter<number>();
for (let j = 0; j < 5; j++) {
emitter.event(e => { sum += e; });
emitter.event(e => {
sum += e;
});
}
});
new RuntimeCase('', () => {
+243 -81
View File
@@ -202,76 +202,156 @@ export class TerminalProxy implements ITerminalProxyCustomMethods, PlaywrightApi
// #region Events
private _onBell = new EventEmitter<void>();
public get onBell(): IEvent<void> { return this._onBell.event; }
public get onBell(): IEvent<void> {
return this._onBell.event;
}
private _onBinary = new EventEmitter<string>();
public get onBinary(): IEvent<string> { return this._onBinary.event; }
public get onBinary(): IEvent<string> {
return this._onBinary.event;
}
private _onCursorMove = new EventEmitter<void>();
public get onCursorMove(): IEvent<void> { return this._onCursorMove.event; }
public get onCursorMove(): IEvent<void> {
return this._onCursorMove.event;
}
private _onData = new EventEmitter<string>();
public get onData(): IEvent<string> { return this._onData.event; }
public get onData(): IEvent<string> {
return this._onData.event;
}
private _onKey = new EventEmitter<{ key: string, domEvent: KeyboardEvent }>();
public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._onKey.event; }
public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> {
return this._onKey.event;
}
private _onLineFeed = new EventEmitter<void>();
public get onLineFeed(): IEvent<void> { return this._onLineFeed.event; }
public get onLineFeed(): IEvent<void> {
return this._onLineFeed.event;
}
private _onRender = new EventEmitter<{ start: number, end: number }>();
public get onRender(): IEvent<{ start: number, end: number }> { return this._onRender.event; }
public get onRender(): IEvent<{ start: number, end: number }> {
return this._onRender.event;
}
private _onResize = new EventEmitter<{ cols: number, rows: number }>();
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._onResize.event; }
public get onResize(): IEvent<{ cols: number, rows: number }> {
return this._onResize.event;
}
private _onScroll = new EventEmitter<number>();
public get onScroll(): IEvent<number> { return this._onScroll.event; }
public get onScroll(): IEvent<number> {
return this._onScroll.event;
}
private _onDimensionsChange = new EventEmitter<IRenderDimensions>();
public get onDimensionsChange(): IEvent<IRenderDimensions> { return this._onDimensionsChange.event; }
public get onDimensionsChange(): IEvent<IRenderDimensions> {
return this._onDimensionsChange.event;
}
private _onSelectionChange = new EventEmitter<void>();
public get onSelectionChange(): IEvent<void> { return this._onSelectionChange.event; }
public get onSelectionChange(): IEvent<void> {
return this._onSelectionChange.event;
}
private _onTitleChange = new EventEmitter<string>();
public get onTitleChange(): IEvent<string> { return this._onTitleChange.event; }
public get onTitleChange(): IEvent<string> {
return this._onTitleChange.event;
}
private _onWriteParsed = new EventEmitter<void>();
public get onWriteParsed(): IEvent<void> { return this._onWriteParsed.event; }
public get onWriteParsed(): IEvent<void> {
return this._onWriteParsed.event;
}
// #endregion
// #region Simple properties
public get cols(): Promise<number> { return this.evaluate(([term]) => term.cols); }
public get rows(): Promise<number> { return this.evaluate(([term]) => term.rows); }
public get modes(): Promise<IModes> { return this.evaluate(([term]) => term.modes); }
public get dimensions(): Promise<IRenderDimensions | undefined> { return this.evaluate(([term]) => term.dimensions); }
public get cols(): Promise<number> {
return this.evaluate(([term]) => term.cols);
}
public get rows(): Promise<number> {
return this.evaluate(([term]) => term.rows);
}
public get modes(): Promise<IModes> {
return this.evaluate(([term]) => term.modes);
}
public get dimensions(): Promise<IRenderDimensions | undefined> {
return this.evaluate(([term]) => term.dimensions);
}
// #endregion
// #region Complex properties
public get buffer(): TerminalBufferNamespaceProxy { return new TerminalBufferNamespaceProxy(this._page, this); }
public get buffer(): TerminalBufferNamespaceProxy {
return new TerminalBufferNamespaceProxy(this._page, this);
}
/**
* Exposes somewhat unsafe access to internals for testing things difficult to do with the regular
* API
*/
public get core(): TerminalCoreProxy { return new TerminalCoreProxy(this._page, this); }
public get core(): TerminalCoreProxy {
return new TerminalCoreProxy(this._page, this);
}
// #endregion
// #region Proxied methods
public async dispose(): Promise<void> { return this.evaluate(([term]) => term.dispose()); }
public async reset(): Promise<void> { return this.evaluate(([term]) => term.reset()); }
public async clear(): Promise<void> { return this.evaluate(([term]) => term.clear()); }
public async focus(): Promise<void> { return this.evaluate(([term]) => term.focus()); }
public async blur(): Promise<void> { return this.evaluate(([term]) => term.blur()); }
public async hasSelection(): Promise<boolean> { return this.evaluate(([term]) => term.hasSelection()); }
public async getSelection(): Promise<string> { return this.evaluate(([term]) => term.getSelection()); }
public async getSelectionPosition(): Promise<IBufferRange | undefined> { return this.evaluate(([term]) => term.getSelectionPosition()); }
public async selectAll(): Promise<void> { return this.evaluate(([term]) => term.selectAll()); }
public async selectLines(start: number, end: number): Promise<void> { return this._page.evaluate(([term, start, end]) => term.selectLines(start, end), [await this.getHandle(), start, end] as const); }
public async clearSelection(): Promise<void> { return this.evaluate(([term]) => term.clearSelection()); }
public async select(column: number, row: number, length: number): Promise<void> { return this._page.evaluate(([term, column, row, length]) => term.select(column, row, length), [await this.getHandle(), column, row, length] as const); }
public async paste(data: string): Promise<void> { return this._page.evaluate(([term, data]) => term.paste(data), [await this.getHandle(), data] as const); }
public async refresh(start: number, end: number): Promise<void> { return this._page.evaluate(([term, start, end]) => term.refresh(start, end), [await this.getHandle(), start, end] as const); }
public async getOption<T extends keyof ITerminalOptions>(key: T): Promise<ITerminalOptions[T]> { return this._page.evaluate(([term, key]) => term.options[key as T], [await this.getHandle(), key] as const); }
public async setOption<T extends keyof ITerminalOptions>(key: T, value: ITerminalOptions[T]): Promise<any> { return this._page.evaluate(([term, key, value]) => term.options[key as T] = (value as ITerminalOptions[T]), [await this.getHandle(), key, value] as const); }
public async dispose(): Promise<void> {
return this.evaluate(([term]) => term.dispose());
}
public async reset(): Promise<void> {
return this.evaluate(([term]) => term.reset());
}
public async clear(): Promise<void> {
return this.evaluate(([term]) => term.clear());
}
public async focus(): Promise<void> {
return this.evaluate(([term]) => term.focus());
}
public async blur(): Promise<void> {
return this.evaluate(([term]) => term.blur());
}
public async hasSelection(): Promise<boolean> {
return this.evaluate(([term]) => term.hasSelection());
}
public async getSelection(): Promise<string> {
return this.evaluate(([term]) => term.getSelection());
}
public async getSelectionPosition(): Promise<IBufferRange | undefined> {
return this.evaluate(([term]) => term.getSelectionPosition());
}
public async selectAll(): Promise<void> {
return this.evaluate(([term]) => term.selectAll());
}
public async selectLines(start: number, end: number): Promise<void> {
return this._page.evaluate(([term, start, end]) => term.selectLines(start, end), [await this.getHandle(), start, end] as const);
}
public async clearSelection(): Promise<void> {
return this.evaluate(([term]) => term.clearSelection());
}
public async select(column: number, row: number, length: number): Promise<void> {
return this._page.evaluate(([term, column, row, length]) => term.select(column, row, length), [await this.getHandle(), column, row, length] as const);
}
public async paste(data: string): Promise<void> {
return this._page.evaluate(([term, data]) => term.paste(data), [await this.getHandle(), data] as const);
}
public async refresh(start: number, end: number): Promise<void> {
return this._page.evaluate(([term, start, end]) => term.refresh(start, end), [await this.getHandle(), start, end] as const);
}
public async getOption<T extends keyof ITerminalOptions>(key: T): Promise<ITerminalOptions[T]> {
return this._page.evaluate(([term, key]) => term.options[key as T], [await this.getHandle(), key] as const);
}
public async setOption<T extends keyof ITerminalOptions>(key: T, value: ITerminalOptions[T]): Promise<any> {
return this._page.evaluate(([term, key, value]) => term.options[key as T] = (value as ITerminalOptions[T]), [await this.getHandle(), key, value] as const);
}
public async setOptions(value: Partial<ITerminalOptions>): Promise<any> {
return this._page.evaluate(([term, value]) => {
term.options = value;
}, [await this.getHandle(), value] as const);
}
public async scrollToTop(): Promise<void> { return this.evaluate(([term]) => term.scrollToTop()); }
public async scrollToBottom(): Promise<void> { return this.evaluate(([term]) => term.scrollToBottom()); }
public async scrollPages(pageCount: number): Promise<void> { return this._page.evaluate(([term, pageCount]) => term.scrollPages(pageCount), [await this.getHandle(), pageCount] as const); }
public async scrollToLine(line: number): Promise<void> { return this._page.evaluate(([term, line]) => term.scrollToLine(line), [await this.getHandle(), line] as const); }
public async scrollLines(amount: number): Promise<void> { return this._page.evaluate(([term, amount]) => term.scrollLines(amount), [await this.getHandle(), amount] as const); }
public async scrollToTop(): Promise<void> {
return this.evaluate(([term]) => term.scrollToTop());
}
public async scrollToBottom(): Promise<void> {
return this.evaluate(([term]) => term.scrollToBottom());
}
public async scrollPages(pageCount: number): Promise<void> {
return this._page.evaluate(([term, pageCount]) => term.scrollPages(pageCount), [await this.getHandle(), pageCount] as const);
}
public async scrollToLine(line: number): Promise<void> {
return this._page.evaluate(([term, line]) => term.scrollToLine(line), [await this.getHandle(), line] as const);
}
public async scrollLines(amount: number): Promise<void> {
return this._page.evaluate(([term, amount]) => term.scrollLines(amount), [await this.getHandle(), amount] as const);
}
public async write(data: string | Uint8Array): Promise<void> {
return this._page.evaluate(([term, data]) => {
return new Promise(r => term.write(typeof data === 'string' ? data : new Uint8Array(data), r));
@@ -282,11 +362,21 @@ export class TerminalProxy implements ITerminalProxyCustomMethods, PlaywrightApi
return new Promise(r => term.writeln(typeof data === 'string' ? data : new Uint8Array(data), r));
}, [await this.getHandle(), typeof data === 'string' ? data : Array.from(data)] as const);
}
public async input(data: string, wasUserInput: boolean = true): Promise<void> { return this.evaluate(([term]) => term.input(data, wasUserInput)); }
public async resize(cols: number, rows: number): Promise<void> { return this._page.evaluate(([term, cols, rows]) => term.resize(cols, rows), [await this.getHandle(), cols, rows] as const); }
public async registerMarker(y?: number | undefined): Promise<IMarker> { return this._page.evaluate(([term, y]) => term.registerMarker(y), [await this.getHandle(), y] as const); }
public async registerDecoration(decorationOptions: IDecorationOptions): Promise<IDecoration | undefined> { return this._page.evaluate(([term, decorationOptions]) => term.registerDecoration(decorationOptions), [await this.getHandle(), decorationOptions] as const); }
public async clearTextureAtlas(): Promise<void> { return this.evaluate(([term]) => term.clearTextureAtlas()); }
public async input(data: string, wasUserInput: boolean = true): Promise<void> {
return this.evaluate(([term]) => term.input(data, wasUserInput));
}
public async resize(cols: number, rows: number): Promise<void> {
return this._page.evaluate(([term, cols, rows]) => term.resize(cols, rows), [await this.getHandle(), cols, rows] as const);
}
public async registerMarker(y?: number | undefined): Promise<IMarker> {
return this._page.evaluate(([term, y]) => term.registerMarker(y), [await this.getHandle(), y] as const);
}
public async registerDecoration(decorationOptions: IDecorationOptions): Promise<IDecoration | undefined> {
return this._page.evaluate(([term, decorationOptions]) => term.registerDecoration(decorationOptions), [await this.getHandle(), decorationOptions] as const);
}
public async clearTextureAtlas(): Promise<void> {
return this.evaluate(([term]) => term.clearTextureAtlas());
}
// #endregion
public async evaluate<T>(pageFunction: PageFunction<Terminal[], T>): Promise<T> {
@@ -314,9 +404,15 @@ class TerminalBufferNamespaceProxy implements PlaywrightApiProxy<IBufferNamespac
}
public get active(): TerminalBufferProxy { return new TerminalBufferProxy(this._page, this._proxy, this._proxy.evaluateHandle(([term]) => term.buffer.active)); }
public get normal(): TerminalBufferProxy { return new TerminalBufferProxy(this._page, this._proxy, this._proxy.evaluateHandle(([term]) => term.buffer.normal)); }
public get alternate(): TerminalBufferProxy { return new TerminalBufferProxy(this._page, this._proxy, this._proxy.evaluateHandle(([term]) => term.buffer.alternate)); }
public get active(): TerminalBufferProxy {
return new TerminalBufferProxy(this._page, this._proxy, this._proxy.evaluateHandle(([term]) => term.buffer.active));
}
public get normal(): TerminalBufferProxy {
return new TerminalBufferProxy(this._page, this._proxy, this._proxy.evaluateHandle(([term]) => term.buffer.normal));
}
public get alternate(): TerminalBufferProxy {
return new TerminalBufferProxy(this._page, this._proxy, this._proxy.evaluateHandle(([term]) => term.buffer.alternate));
}
}
// TODO: Adopt PlaywrightApiProxy
@@ -328,12 +424,24 @@ class TerminalBufferProxy /* implements EnsureAsyncProperties<IBuffer>*/ {
) {
}
public get type(): Promise<'normal' | 'alternate'> { return this.evaluate(([buffer]) => buffer.type); }
public get cursorY(): Promise<number> { return this.evaluate(([buffer]) => buffer.cursorY); }
public get cursorX(): Promise<number> { return this.evaluate(([buffer]) => buffer.cursorX); }
public get viewportY(): Promise<number> { return this.evaluate(([buffer]) => buffer.viewportY); }
public get baseY(): Promise<number> { return this.evaluate(([buffer]) => buffer.baseY); }
public get length(): Promise<number> { return this.evaluate(([buffer]) => buffer.length); }
public get type(): Promise<'normal' | 'alternate'> {
return this.evaluate(([buffer]) => buffer.type);
}
public get cursorY(): Promise<number> {
return this.evaluate(([buffer]) => buffer.cursorY);
}
public get cursorX(): Promise<number> {
return this.evaluate(([buffer]) => buffer.cursorX);
}
public get viewportY(): Promise<number> {
return this.evaluate(([buffer]) => buffer.viewportY);
}
public get baseY(): Promise<number> {
return this.evaluate(([buffer]) => buffer.baseY);
}
public get length(): Promise<number> {
return this.evaluate(([buffer]) => buffer.length);
}
public async getLine(y: number): Promise<TerminalBufferLine | undefined> {
const lineHandle = await this._page.evaluateHandle(([buffer, y]) => buffer.getLine(y), [await this._handle, y] as const);
const value = await lineHandle.jsonValue();
@@ -356,8 +464,12 @@ class TerminalBufferLine {
) {
}
public get length(): Promise<number> { return this.evaluate(([bufferLine]) => bufferLine.length); }
public get isWrapped(): Promise<boolean> { return this.evaluate(([bufferLine]) => bufferLine.isWrapped); }
public get length(): Promise<number> {
return this.evaluate(([bufferLine]) => bufferLine.length);
}
public get isWrapped(): Promise<boolean> {
return this.evaluate(([bufferLine]) => bufferLine.isWrapped);
}
public translateToString(trimRight?: boolean, startColumn?: number, endColumn?: number): Promise<string> {
return this._page.evaluate(([bufferLine, trimRight, startColumn, endColumn]) => {
@@ -387,33 +499,79 @@ class TerminalBufferCell {
) {
}
public getWidth(): Promise<number> { return this.evaluate(([cell]) => cell.getWidth()); }
public getChars(): Promise<string> { return this.evaluate(([cell]) => cell.getChars()); }
public getCode(): Promise<number> { return this.evaluate(([cell]) => cell.getCode()); }
public getWidth(): Promise<number> {
return this.evaluate(([cell]) => cell.getWidth());
}
public getChars(): Promise<string> {
return this.evaluate(([cell]) => cell.getChars());
}
public getCode(): Promise<number> {
return this.evaluate(([cell]) => cell.getCode());
}
public getFgColorMode(): Promise<number> { return this.evaluate(([cell]) => cell.getFgColorMode()); }
public getBgColorMode(): Promise<number> { return this.evaluate(([cell]) => cell.getBgColorMode()); }
public getFgColor(): Promise<number> { return this.evaluate(([cell]) => cell.getFgColor()); }
public getBgColor(): Promise<number> { return this.evaluate(([cell]) => cell.getBgColor()); }
public getFgColorMode(): Promise<number> {
return this.evaluate(([cell]) => cell.getFgColorMode());
}
public getBgColorMode(): Promise<number> {
return this.evaluate(([cell]) => cell.getBgColorMode());
}
public getFgColor(): Promise<number> {
return this.evaluate(([cell]) => cell.getFgColor());
}
public getBgColor(): Promise<number> {
return this.evaluate(([cell]) => cell.getBgColor());
}
public isBold(): Promise<number> { return this.evaluate(([cell]) => cell.isBold()); }
public isItalic(): Promise<number> { return this.evaluate(([cell]) => cell.isItalic()); }
public isDim(): Promise<number> { return this.evaluate(([cell]) => cell.isDim()); }
public isUnderline(): Promise<number> { return this.evaluate(([cell]) => cell.isUnderline()); }
public isBlink(): Promise<number> { return this.evaluate(([cell]) => cell.isBlink()); }
public isInverse(): Promise<number> { return this.evaluate(([cell]) => cell.isInverse()); }
public isInvisible(): Promise<number> { return this.evaluate(([cell]) => cell.isInvisible()); }
public isStrikethrough(): Promise<number> { return this.evaluate(([cell]) => cell.isStrikethrough()); }
public isOverline(): Promise<number> { return this.evaluate(([cell]) => cell.isOverline()); }
public isBold(): Promise<number> {
return this.evaluate(([cell]) => cell.isBold());
}
public isItalic(): Promise<number> {
return this.evaluate(([cell]) => cell.isItalic());
}
public isDim(): Promise<number> {
return this.evaluate(([cell]) => cell.isDim());
}
public isUnderline(): Promise<number> {
return this.evaluate(([cell]) => cell.isUnderline());
}
public isBlink(): Promise<number> {
return this.evaluate(([cell]) => cell.isBlink());
}
public isInverse(): Promise<number> {
return this.evaluate(([cell]) => cell.isInverse());
}
public isInvisible(): Promise<number> {
return this.evaluate(([cell]) => cell.isInvisible());
}
public isStrikethrough(): Promise<number> {
return this.evaluate(([cell]) => cell.isStrikethrough());
}
public isOverline(): Promise<number> {
return this.evaluate(([cell]) => cell.isOverline());
}
public isFgRGB(): Promise<boolean> { return this.evaluate(([cell]) => cell.isFgRGB()); }
public isBgRGB(): Promise<boolean> { return this.evaluate(([cell]) => cell.isBgRGB()); }
public isFgPalette(): Promise<boolean> { return this.evaluate(([cell]) => cell.isFgPalette()); }
public isBgPalette(): Promise<boolean> { return this.evaluate(([cell]) => cell.isBgPalette()); }
public isFgDefault(): Promise<boolean> { return this.evaluate(([cell]) => cell.isFgDefault()); }
public isBgDefault(): Promise<boolean> { return this.evaluate(([cell]) => cell.isBgDefault()); }
public isFgRGB(): Promise<boolean> {
return this.evaluate(([cell]) => cell.isFgRGB());
}
public isBgRGB(): Promise<boolean> {
return this.evaluate(([cell]) => cell.isBgRGB());
}
public isFgPalette(): Promise<boolean> {
return this.evaluate(([cell]) => cell.isFgPalette());
}
public isBgPalette(): Promise<boolean> {
return this.evaluate(([cell]) => cell.isBgPalette());
}
public isFgDefault(): Promise<boolean> {
return this.evaluate(([cell]) => cell.isFgDefault());
}
public isBgDefault(): Promise<boolean> {
return this.evaluate(([cell]) => cell.isBgDefault());
}
public isAttributeDefault(): Promise<boolean> { return this.evaluate(([cell]) => cell.isAttributeDefault()); }
public isAttributeDefault(): Promise<boolean> {
return this.evaluate(([cell]) => cell.isAttributeDefault());
}
public async evaluate<T>(pageFunction: PageFunction<IBufferCell[], T>): Promise<T> {
return this._page.evaluate(pageFunction, [this._handle]);
@@ -427,8 +585,12 @@ class TerminalCoreProxy {
) {
}
public get isDisposed(): Promise<boolean> { return this.evaluate(([core]) => (core as any)._isDisposed); }
public get renderDimensions(): Promise<IRenderDimensionsInternal> { return this.evaluate(([core]) => ((core as any)._renderService as IRenderService).dimensions); }
public get isDisposed(): Promise<boolean> {
return this.evaluate(([core]) => (core as any)._isDisposed);
}
public get renderDimensions(): Promise<IRenderDimensionsInternal> {
return this.evaluate(([core]) => ((core as any)._renderService as IRenderService).dimensions);
}
public async triggerBinaryEvent(data: string): Promise<void> {
return this._page.evaluate(([core, data]) => core.coreService.triggerBinaryEvent(data), [await this._getCoreHandle(), data] as const);