Comparing version 2.9.2 to 3.0.1
@@ -42,4 +42,3 @@ export declare enum Modifier { | ||
} | ||
export declare class KeyParser { | ||
emit: (keys: Key[]) => void; | ||
export declare class KeyParser implements AsyncIterator<Key>, AsyncIterable<Key> { | ||
state: State; | ||
@@ -49,7 +48,13 @@ modifiers: Modifier; | ||
lastKey: number; | ||
constructor(emit: (keys: Key[]) => void); | ||
queue: Key[]; | ||
resolve: ((value: IteratorResult<Key>) => void) | undefined; | ||
ended: boolean; | ||
[Symbol.asyncIterator](): this; | ||
next(): Promise<IteratorResult<Key>>; | ||
private wake; | ||
end(): void; | ||
feed(s: string): void; | ||
feedCodepoint(c: number, rv: Key[]): boolean; | ||
parseCsi(rv: Key[], command: string, args: number[]): void; | ||
feedCodepoint(c: number): boolean; | ||
parseCsi(command: string, args: number[]): void; | ||
} | ||
export {}; |
@@ -11,15 +11,2 @@ "use strict"; | ||
})(Modifier = exports.Modifier || (exports.Modifier = {})); | ||
function modifiersFromFlags(n) { | ||
const rv = []; | ||
n -= 1; | ||
if (n & 8) | ||
rv.push(Modifier.Meta); | ||
if (n & 2) | ||
rv.push(Modifier.Alt); | ||
if (n & 1) | ||
rv.push(Modifier.Shift); | ||
if (n & 4) | ||
rv.push(Modifier.Control); | ||
return rv; | ||
} | ||
var KeyType; | ||
@@ -113,4 +100,3 @@ (function (KeyType) { | ||
class KeyParser { | ||
constructor(emit) { | ||
this.emit = emit; | ||
constructor() { | ||
this.state = State.Normal; | ||
@@ -120,13 +106,35 @@ this.modifiers = 0; | ||
this.lastKey = Date.now(); | ||
// pass | ||
// async iterator state: queued keys or waiting reader | ||
this.queue = []; | ||
this.ended = false; | ||
} | ||
[Symbol.asyncIterator]() { | ||
return this; | ||
} | ||
next() { | ||
return new Promise(resolve => { | ||
this.resolve = resolve; | ||
this.wake(); | ||
}); | ||
} | ||
// check if we should hand out keys to a waiting reader | ||
wake() { | ||
if (!this.resolve || (!this.ended && this.queue.length == 0)) | ||
return; | ||
const resolve = this.resolve; | ||
this.resolve = undefined; | ||
const value = this.queue.shift(); | ||
resolve({ done: this.ended, value }); | ||
} | ||
end() { | ||
this.ended = true; | ||
this.wake(); | ||
} | ||
feed(s) { | ||
const rv = []; | ||
let checkMeta = false; | ||
for (let c of Array.from(s).map(s => s.codePointAt(0) || 0)) { | ||
checkMeta = this.feedCodepoint(c, rv); | ||
checkMeta = this.feedCodepoint(c); | ||
} | ||
this.lastKey = Date.now(); | ||
if (rv.length > 0) | ||
this.emit(rv); | ||
this.wake(); | ||
if (checkMeta) { | ||
@@ -136,4 +144,5 @@ setTimeout(() => { | ||
// dangling ESC, maybe it was just ESC... | ||
this.emit([new Key(this.modifiers, KeyType.Esc)]); | ||
this.queue.push(new Key(this.modifiers, KeyType.Esc)); | ||
this.state = State.Normal; | ||
this.wake(); | ||
} | ||
@@ -144,3 +153,3 @@ }, ESC_TIMEOUT); | ||
// returns true if it processed a dangling ESC | ||
feedCodepoint(c, rv) { | ||
feedCodepoint(c) { | ||
switch (this.state) { | ||
@@ -150,7 +159,7 @@ case State.Normal: | ||
case Ascii.TAB: | ||
rv.push(new Key(this.modifiers, KeyType.Tab)); | ||
this.queue.push(new Key(this.modifiers, KeyType.Tab)); | ||
this.modifiers = 0; | ||
return false; | ||
case Ascii.CR: | ||
rv.push(new Key(this.modifiers, KeyType.Return)); | ||
this.queue.push(new Key(this.modifiers, KeyType.Return)); | ||
this.modifiers = 0; | ||
@@ -163,3 +172,3 @@ return false; | ||
case Ascii.DEL: | ||
rv.push(new Key(this.modifiers, KeyType.Backspace)); | ||
this.queue.push(new Key(this.modifiers, KeyType.Backspace)); | ||
this.modifiers = 0; | ||
@@ -173,3 +182,3 @@ return false; | ||
} | ||
rv.push(new Key(this.modifiers, KeyType.Normal, String.fromCodePoint(c))); | ||
this.queue.push(new Key(this.modifiers, KeyType.Normal, String.fromCodePoint(c))); | ||
this.modifiers = 0; | ||
@@ -191,3 +200,3 @@ return false; | ||
this.state = State.Normal; | ||
return this.feedCodepoint(c, rv); | ||
return this.feedCodepoint(c); | ||
} | ||
@@ -199,3 +208,3 @@ case State.CSI: | ||
} | ||
this.parseCsi(rv, String.fromCodePoint(c), this.buffer.split(/[;:]/).map(s => parseInt(s, 10))); | ||
this.parseCsi(String.fromCodePoint(c), this.buffer.split(/[;:]/).map(s => parseInt(s, 10))); | ||
this.state = State.Normal; | ||
@@ -206,3 +215,3 @@ this.modifiers = 0; | ||
if (c >= Ascii.P && c <= Ascii.S) { | ||
rv.push(new Key(this.modifiers, KeyType.Function, (1 + c - Ascii.P).toString())); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, (1 + c - Ascii.P).toString())); | ||
this.state = State.Normal; | ||
@@ -214,9 +223,9 @@ this.modifiers = 0; | ||
// what is ESC O (something)? we don't support it. | ||
rv.push(new Key(Modifier.Meta, KeyType.Normal, "O")); | ||
this.queue.push(new Key(Modifier.Meta, KeyType.Normal, "O")); | ||
this.state = State.Normal; | ||
return this.feedCodepoint(c, rv); | ||
return this.feedCodepoint(c); | ||
} | ||
} | ||
} | ||
parseCsi(rv, command, args) { | ||
parseCsi(command, args) { | ||
if (args[0] == 1 && args.length >= 2) | ||
@@ -226,34 +235,34 @@ this.modifiers |= (args[1] - 1); | ||
case "A": | ||
rv.push(new Key(this.modifiers, KeyType.Up)); | ||
this.queue.push(new Key(this.modifiers, KeyType.Up)); | ||
break; | ||
case "B": | ||
rv.push(new Key(this.modifiers, KeyType.Down)); | ||
this.queue.push(new Key(this.modifiers, KeyType.Down)); | ||
break; | ||
case "C": | ||
rv.push(new Key(this.modifiers, KeyType.Right)); | ||
this.queue.push(new Key(this.modifiers, KeyType.Right)); | ||
break; | ||
case "D": | ||
rv.push(new Key(this.modifiers, KeyType.Left)); | ||
this.queue.push(new Key(this.modifiers, KeyType.Left)); | ||
break; | ||
case "H": | ||
rv.push(new Key(this.modifiers, KeyType.Home)); | ||
this.queue.push(new Key(this.modifiers, KeyType.Home)); | ||
break; | ||
case "F": | ||
rv.push(new Key(this.modifiers, KeyType.End)); | ||
this.queue.push(new Key(this.modifiers, KeyType.End)); | ||
break; | ||
case "P": | ||
rv.push(new Key(this.modifiers, KeyType.Function, "1")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "1")); | ||
break; | ||
case "Q": | ||
rv.push(new Key(this.modifiers, KeyType.Function, "2")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "2")); | ||
break; | ||
case "R": | ||
rv.push(new Key(this.modifiers, KeyType.Function, "3")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "3")); | ||
break; | ||
case "S": | ||
rv.push(new Key(this.modifiers, KeyType.Function, "4")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "4")); | ||
break; | ||
case "Z": | ||
// xterm sends a special code for shift-tab! | ||
rv.push(new Key(Modifier.Shift, KeyType.Tab)); | ||
this.queue.push(new Key(Modifier.Shift, KeyType.Tab)); | ||
break; | ||
@@ -265,62 +274,62 @@ case "~": { | ||
case 1: | ||
rv.push(new Key(this.modifiers, KeyType.Home)); | ||
this.queue.push(new Key(this.modifiers, KeyType.Home)); | ||
break; | ||
case 2: | ||
rv.push(new Key(this.modifiers, KeyType.Insert)); | ||
this.queue.push(new Key(this.modifiers, KeyType.Insert)); | ||
break; | ||
case 3: | ||
rv.push(new Key(this.modifiers, KeyType.Delete)); | ||
this.queue.push(new Key(this.modifiers, KeyType.Delete)); | ||
break; | ||
case 4: | ||
rv.push(new Key(this.modifiers, KeyType.End)); | ||
this.queue.push(new Key(this.modifiers, KeyType.End)); | ||
break; | ||
case 5: | ||
rv.push(new Key(this.modifiers, KeyType.PageUp)); | ||
this.queue.push(new Key(this.modifiers, KeyType.PageUp)); | ||
break; | ||
case 6: | ||
rv.push(new Key(this.modifiers, KeyType.PageDown)); | ||
this.queue.push(new Key(this.modifiers, KeyType.PageDown)); | ||
break; | ||
case 11: | ||
rv.push(new Key(this.modifiers, KeyType.Function, "1")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "1")); | ||
break; | ||
case 12: | ||
rv.push(new Key(this.modifiers, KeyType.Function, "2")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "2")); | ||
break; | ||
case 13: | ||
rv.push(new Key(this.modifiers, KeyType.Function, "3")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "3")); | ||
break; | ||
case 14: | ||
rv.push(new Key(this.modifiers, KeyType.Function, "4")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "4")); | ||
break; | ||
case 15: | ||
rv.push(new Key(this.modifiers, KeyType.Function, "5")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "5")); | ||
break; | ||
// what happened to 16? | ||
case 17: | ||
rv.push(new Key(this.modifiers, KeyType.Function, "6")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "6")); | ||
break; | ||
case 18: | ||
rv.push(new Key(this.modifiers, KeyType.Function, "7")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "7")); | ||
break; | ||
case 19: | ||
rv.push(new Key(this.modifiers, KeyType.Function, "8")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "8")); | ||
break; | ||
case 20: | ||
rv.push(new Key(this.modifiers, KeyType.Function, "9")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "9")); | ||
break; | ||
case 21: | ||
rv.push(new Key(this.modifiers, KeyType.Function, "10")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "10")); | ||
break; | ||
// what happened to 22? | ||
case 23: | ||
rv.push(new Key(this.modifiers, KeyType.Function, "11")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "11")); | ||
break; | ||
case 24: | ||
rv.push(new Key(this.modifiers, KeyType.Function, "12")); | ||
this.queue.push(new Key(this.modifiers, KeyType.Function, "12")); | ||
break; | ||
case 200: | ||
rv.push(new Key(this.modifiers, KeyType.PasteBegin)); | ||
this.queue.push(new Key(this.modifiers, KeyType.PasteBegin)); | ||
break; | ||
case 201: | ||
rv.push(new Key(this.modifiers, KeyType.PasteEnd)); | ||
this.queue.push(new Key(this.modifiers, KeyType.PasteEnd)); | ||
break; | ||
@@ -332,4 +341,4 @@ } | ||
// well crap. CSI + garbage? | ||
rv.push(new Key(Modifier.Meta, KeyType.Normal, "[")); | ||
rv.push(new Key(0, KeyType.Normal, command)); | ||
this.queue.push(new Key(Modifier.Meta, KeyType.Normal, "[")); | ||
this.queue.push(new Key(0, KeyType.Normal, command)); | ||
break; | ||
@@ -336,0 +345,0 @@ } |
"use strict"; | ||
var __asyncValues = (this && this.__asyncValues) || function (o) { | ||
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); | ||
var m = o[Symbol.asyncIterator], i; | ||
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); | ||
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } | ||
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -6,38 +13,56 @@ const keys_1 = require("../antsy/keys"); | ||
require("source-map-support/register"); | ||
function keyParser() { | ||
const output = []; | ||
const parser = new keys_1.KeyParser(keys => keys.forEach(k => output.push(k))); | ||
return { parser, output }; | ||
} | ||
function bundle(keys) { | ||
return keys.map(k => k.toString()).join(","); | ||
} | ||
const delay = (msec) => new Promise(resolve => setTimeout(resolve, msec)); | ||
describe("KeyParser", () => { | ||
it("handles ascii", () => { | ||
const { parser, output } = keyParser(); | ||
let parser = new keys_1.KeyParser(); | ||
let output = []; | ||
beforeEach(() => { | ||
parser = new keys_1.KeyParser(); | ||
output = []; | ||
setTimeout(async () => { | ||
var e_1, _a; | ||
try { | ||
for (var parser_1 = __asyncValues(parser), parser_1_1; parser_1_1 = await parser_1.next(), !parser_1_1.done;) { | ||
const key = parser_1_1.value; | ||
output.push(key); | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (parser_1_1 && !parser_1_1.done && (_a = parser_1.return)) await _a.call(parser_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
}, 0); | ||
}); | ||
afterEach(() => { | ||
parser.end(); | ||
}); | ||
it("handles ascii", async () => { | ||
parser.feed("hell"); | ||
parser.feed("o"); | ||
await delay(1); | ||
bundle(output).should.eql("h,e,l,l,o"); | ||
}); | ||
it("control codes", () => { | ||
const { parser, output } = keyParser(); | ||
it("control codes", async () => { | ||
parser.feed("hell\u0001s\u0009\u0008\u000d\u007f"); | ||
await delay(1); | ||
bundle(output).should.eql("h,e,l,l,C-A,s,Tab,Backspace,Return,Backspace"); | ||
}); | ||
it("meta codes", () => { | ||
const { parser, output } = keyParser(); | ||
it("meta codes", async () => { | ||
parser.feed("x\u001bc\u001b[y"); | ||
await delay(1); | ||
bundle(output).should.eql("x,M-c,M-[,y"); | ||
}); | ||
it("raw esc", done => { | ||
const { parser, output } = keyParser(); | ||
it("raw esc", async () => { | ||
parser.feed("\u001b"); | ||
setTimeout(() => { | ||
parser.feed("a"); | ||
bundle(output).should.eql("Esc,a"); | ||
done(); | ||
}, 150); | ||
await delay(150); | ||
parser.feed("a"); | ||
await delay(1); | ||
bundle(output).should.eql("Esc,a"); | ||
}); | ||
it("arrows", () => { | ||
const { parser, output } = keyParser(); | ||
it("arrows", async () => { | ||
parser.feed("\u001b[A"); | ||
@@ -47,6 +72,6 @@ parser.feed("\u001b"); | ||
parser.feed("B\u001b[D"); | ||
await delay(1); | ||
bundle(output).should.eql("Up,Right,Down,Left"); | ||
}); | ||
it("arrows with modifiers", () => { | ||
const { parser, output } = keyParser(); | ||
it("arrows with modifiers", async () => { | ||
parser.feed("\u001b[1;2A"); | ||
@@ -56,34 +81,35 @@ parser.feed("\u001b[1;5A"); | ||
parser.feed("\u001b\u001b[1;2A"); | ||
await delay(1); | ||
bundle(output).should.eql("S-Up,C-Up,S-C-Up,M-S-Up"); | ||
}); | ||
it("home/end", () => { | ||
const { parser, output } = keyParser(); | ||
it("home/end", async () => { | ||
parser.feed("\u001b[H\u001b[F\u001b[1~\u001b[4~"); | ||
await delay(1); | ||
bundle(output).should.eql("Home,End,Home,End"); | ||
}); | ||
it("ins/del/pgup/pgdn", () => { | ||
const { parser, output } = keyParser(); | ||
it("ins/del/pgup/pgdn", async () => { | ||
parser.feed("\u001b[2~\u001b[3~\u001b[5~\u001b[6~"); | ||
await delay(1); | ||
bundle(output).should.eql("Insert,Delete,PageUp,PageDown"); | ||
}); | ||
it("old f-keys", () => { | ||
const { parser, output } = keyParser(); | ||
it("old f-keys", async () => { | ||
parser.feed("\u001bOP\u001bOQ\u001bOR\u001bOS"); | ||
await delay(1); | ||
bundle(output).should.eql("F1,F2,F3,F4"); | ||
}); | ||
it("new f-keys", () => { | ||
const { parser, output } = keyParser(); | ||
it("new f-keys", async () => { | ||
parser.feed("\u001b[11~\u001b[12~\u001b[13~\u001b[14~\u001b[15~"); | ||
parser.feed("\u001b[17~\u001b[18~\u001b[19~\u001b[20~\u001b[21~"); | ||
parser.feed("\u001b[23~\u001b[24~"); | ||
await delay(1); | ||
bundle(output).should.eql("F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,F11,F12"); | ||
}); | ||
it("f-keys with modifiers", () => { | ||
const { parser, output } = keyParser(); | ||
it("f-keys with modifiers", async () => { | ||
parser.feed("\u001b[11;2~\u001b[12;5~\u001b[13;6~\u001b[1;2P\u001b\u001b[1;5Q"); | ||
await delay(1); | ||
bundle(output).should.eql("S-F1,C-F2,S-C-F3,S-F1,M-C-F2"); | ||
}); | ||
it("paste", () => { | ||
const { parser, output } = keyParser(); | ||
it("paste", async () => { | ||
parser.feed("\u001b[200~password\u001b[201~"); | ||
await delay(1); | ||
bundle(output).should.eql("Paste,p,a,s,s,w,o,r,d,/Paste"); | ||
@@ -90,0 +116,0 @@ }); |
{ | ||
"name": "antsy", | ||
"version": "2.9.2", | ||
"version": "3.0.1", | ||
"description": "draw full-color (xterm-256) ansi graphics into a buffer", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -136,3 +136,3 @@ # Antsy | ||
+-----------------------+ | ||
```` | ||
``` | ||
@@ -190,2 +190,33 @@ ...you could use a grid layout with 2 columns and 2 rows. The left column and top row are stretchy: | ||
## KeyParser | ||
Antsy can also parse a stream of xterm/VT input and convert it into key events. | ||
- `new KeyParser() // implements AsyncIterator<Key>, AsyncIterable<Key>` | ||
Feed incoming bytes: | ||
- `feed(s: string): void` | ||
Parsed key events will emerge on the async iterator as `Key` objects. Each `Key` object contains three fields: | ||
- `modifiers: Modifier` | ||
A bitmap of modifier keys: `Shift`, `Alt`, `Control`, `Meta` | ||
- `type: KeyType` | ||
Either `Normal` for a common ASCII symbol (like "a" or "7" or ":"), or one of: | ||
- `Up`, `Down`, `Left`, `Right` arrow keys | ||
- `PageUp`, `PageDown`, `Home`, `End` extreme arrow keys | ||
- `Insert`, `Delete` vestigial IBM keys | ||
- `Tab`, `Return`, `Esc`, `Backspace` | ||
- `Function` (with `key` being "1" through "12") | ||
- `PasteBegin`, `PasteEnd` to mark xterm paste boundaries | ||
- `key: string` | ||
The ASCII key pressed, or the number of the function key, or "". | ||
## How it works | ||
@@ -192,0 +223,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
209307
45
2390
246