From 3a866cf202bbf5a797d927547cf27b391db607a9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 9 Jun 2016 18:41:54 -0700 Subject: [PATCH] Refactor escape sequence code and add tests --- src/xterm.js | 200 ++++++++++++++++++++++++--------------------------- test/test.js | 19 ++++- 2 files changed, 109 insertions(+), 110 deletions(-) diff --git a/src/xterm.js b/src/xterm.js index d8bdd7ac..f4a1d3d0 100644 --- a/src/xterm.js +++ b/src/xterm.js @@ -2301,229 +2301,213 @@ // Key Resources: // https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent Terminal.prototype.keyDown = function(ev) { - var self = this, key; + var self = this; + var result = this.evaluateKeyEscapeSequence(ev); + if (result.scrollDisp) { + this.scrollDisp(result.scrollDisp); + return this.cancel(ev); + } + + if (result.cancel) { + // The event is canceled at the end already, is this necessary? + this.cancel(ev, true); + } + + if (!result.key || (this.isMac && ev.metaKey)) { + return true; + } + + this.emit('keydown', ev); + this.emit('key', result.key, ev); + this.showCursor(); + this.handler(result.key); + + return this.cancel(ev, true); + }; + + Terminal.prototype.evaluateKeyEscapeSequence = function(ev) { + var result = { + // Whether to cancel event propogation (NOTE: this may not be needed since the event is + // canceled at the end of keyDown + cancel: false, + // The new key even to emit + key: undefined, + // The number of characters to scroll, if this is defined it will cancel the event + scrollDisp: undefined + }; switch (ev.keyCode) { // backspace case 8: if (ev.shiftKey) { - key = '\x08'; // ^H + result.key = '\x08'; // ^H break; } - key = '\x7f'; // ^? + result.key = '\x7f'; // ^? break; // tab case 9: if (ev.shiftKey) { - key = '\x1b[Z'; + result.key = '\x1b[Z'; break; } - key = '\t'; - this.cancel(ev, true); + result.key = '\t'; + result.cancel = true; break; // return/enter case 13: - key = '\r'; - this.cancel(ev, true); + result.key = '\r'; + result.cancel = true; break; // escape case 27: - key = '\x1b'; - this.cancel(ev, true); + result.key = '\x1b'; + result.cancel = true; break; // left-arrow case 37: if (ev.altKey) { - this.cancel(ev, true); - key = '\x1bb' // Jump a word back + result.key = '\x1bb' // Jump a word back + result.cancel = true; break; } if (ev.ctrlKey) { - key = '\x1b[5D'; // Jump a word back + result.key = '\x1b[5D'; // Jump a word back break; } if (this.applicationCursor) { - key = '\x1bOD'; // SS3 as ^[O for 7-bit + result.key = '\x1bOD'; // SS3 as ^[O for 7-bit break; } - key = '\x1b[D'; + result.key = '\x1b[D'; break; // right-arrow case 39: if (ev.altKey) { - this.cancel(ev, true); - key = '\x1bf' // Jump a word forward + result.key = '\x1bf' // Jump a word forward + result.cancel = true; break; } if (ev.ctrlKey) { - key = '\x1b[5C'; // Jump a word forward + result.key = '\x1b[5C'; // Jump a word forward break; } if (this.applicationCursor) { - key = '\x1bOC'; + result.key = '\x1bOC'; break; } - key = '\x1b[C'; + result.key = '\x1b[C'; break; // up-arrow case 38: if (this.applicationCursor) { - key = '\x1bOA'; + result.key = '\x1bOA'; break; } if (ev.ctrlKey) { - this.scrollDisp(-1); - return this.cancel(ev); + result.scrollDisp = -1; } else { - key = '\x1b[A'; + result.key = '\x1b[A'; } break; // down-arrow case 40: if (this.applicationCursor) { - key = '\x1bOB'; + result.key = '\x1bOB'; break; } if (ev.ctrlKey) { - this.scrollDisp(1); - return this.cancel(ev); + result.scrollDisp = 1; } else { - key = '\x1b[B'; + result.key = '\x1b[B'; } break; // delete case 46: - key = '\x1b[3~'; + result.key = '\x1b[3~'; break; // insert case 45: - key = '\x1b[2~'; + result.key = '\x1b[2~'; break; // home case 36: if (this.applicationKeypad) { - key = '\x1bOH'; + result.key = '\x1bOH'; break; } - key = '\x1bOH'; + result.key = '\x1bOH'; break; // end case 35: if (this.applicationKeypad) { - key = '\x1bOF'; + result.key = '\x1bOF'; break; } - key = '\x1bOF'; + result.key = '\x1bOF'; break; // page up case 33: if (ev.shiftKey) { - this.scrollDisp(-(this.rows - 1)); - return this.cancel(ev); + result.scrollDisp = -(this.rows - 1); } else { - key = '\x1b[5~'; + result.key = '\x1b[5~'; } break; // page down case 34: if (ev.shiftKey) { - this.scrollDisp(this.rows - 1); - return this.cancel(ev); + result.scrollDisp = this.rows - 1; } else { - key = '\x1b[6~'; + result.key = '\x1b[6~'; } break; - // F1 - case 112: - key = '\x1bOP'; - break; - // F2 - case 113: - key = '\x1bOQ'; - break; - // F3 - case 114: - key = '\x1bOR'; - break; - // F4 - case 115: - key = '\x1bOS'; - break; - // F5 - case 116: - key = '\x1b[15~'; - break; - // F6 - case 117: - key = '\x1b[17~'; - break; - // F7 - case 118: - key = '\x1b[18~'; - break; - // F8 - case 119: - key = '\x1b[19~'; - break; - // F9 - case 120: - key = '\x1b[20~'; - break; - // F10 - case 121: - key = '\x1b[21~'; - break; - // F11 - case 122: - key = '\x1b[23~'; - break; - // F12 - case 123: - key = '\x1b[24~'; - break; + // F1-F12 + case 112: result.key = '\x1bOP'; break; + case 113: result.key = '\x1bOQ'; break; + case 114: result.key = '\x1bOR'; break; + case 115: result.key = '\x1bOS'; break; + case 116: result.key = '\x1b[15~'; break; + case 117: result.key = '\x1b[17~'; break; + case 118: result.key = '\x1b[18~'; break; + case 119: result.key = '\x1b[19~'; break; + case 120: result.key = '\x1b[20~'; break; + case 121: result.key = '\x1b[21~'; break; + case 122: result.key = '\x1b[23~'; break; + case 123: result.key = '\x1b[24~'; break; default: // a-z and space if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) { if (ev.keyCode >= 65 && ev.keyCode <= 90) { - key = String.fromCharCode(ev.keyCode - 64); + result.key = String.fromCharCode(ev.keyCode - 64); } else if (ev.keyCode === 32) { // NUL - key = String.fromCharCode(0); + result.key = String.fromCharCode(0); } else if (ev.keyCode >= 51 && ev.keyCode <= 55) { // escape, file sep, group sep, record sep, unit sep - key = String.fromCharCode(ev.keyCode - 51 + 27); + result.key = String.fromCharCode(ev.keyCode - 51 + 27); } else if (ev.keyCode === 56) { // delete - key = String.fromCharCode(127); + result.key = String.fromCharCode(127); } else if (ev.keyCode === 219) { // ^[ - escape - key = String.fromCharCode(27); + result.key = String.fromCharCode(27); } else if (ev.keyCode === 221) { // ^] - group sep - key = String.fromCharCode(29); + result.key = String.fromCharCode(29); } } else if ((!this.isMac && ev.altKey) || (this.isMac && ev.metaKey)) { if (ev.keyCode >= 65 && ev.keyCode <= 90) { - key = '\x1b' + String.fromCharCode(ev.keyCode + 32); + result.key = '\x1b' + String.fromCharCode(ev.keyCode + 32); } else if (ev.keyCode === 192) { - key = '\x1b`'; + result.key = '\x1b`'; } else if (ev.keyCode >= 48 && ev.keyCode <= 57) { - key = '\x1b' + (ev.keyCode - 48); + result.key = '\x1b' + (ev.keyCode - 48); } } break; } - - if (!key || (this.isMac && ev.metaKey)) { - return true; - } - - this.emit('keydown', ev); - this.emit('key', key, ev); - this.showCursor(); - this.handler(key); - - return this.cancel(ev, true); + return result; }; Terminal.prototype.setgLevel = function(g) { diff --git a/test/test.js b/test/test.js index d746eb28..7297d8af 100644 --- a/test/test.js +++ b/test/test.js @@ -1,6 +1,21 @@ var assert = require('chai').assert; +var Terminal = require('../src/xterm'); +describe('xterm.js', function() { + var xterm; -describe('xterm', function() { - // Just a dummy first test + beforeEach(function () { + xterm = new Terminal(); + }); + + describe('evaluateKeyEscapeSequence', function() { + it('should return \\x1b[5D when ctrl+left is passed', function() { + var event = { ctrlKey: true, keyCode: 37 }; + assert.equal(xterm.evaluateKeyEscapeSequence(event).key, '\x1b[5D'); + }); + it('should return \\x1b[5C when ctrl+right is passed', function() { + var event = { ctrlKey: true, keyCode: 39 }; + assert.equal(xterm.evaluateKeyEscapeSequence(event).key, '\x1b[5C'); + }); + }); });