macgyver 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +45 -0
- data/Rakefile +1 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Growl +0 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Growl +0 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/Growl.h +5 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h +551 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h +341 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Resources/Info.plist +40 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/_CodeSignature/CodeResources +34 -0
- data/assets/MacGap.app/Contents/Info.plist +48 -0
- data/assets/MacGap.app/Contents/MacOS/MacGap +0 -0
- data/assets/MacGap.app/Contents/PkgInfo +1 -0
- data/assets/MacGap.app/Contents/Resources/_debugger.js +1718 -0
- data/assets/MacGap.app/Contents/Resources/_http_agent.js +310 -0
- data/assets/MacGap.app/Contents/Resources/_http_client.js +533 -0
- data/assets/MacGap.app/Contents/Resources/_http_common.js +222 -0
- data/assets/MacGap.app/Contents/Resources/_http_incoming.js +194 -0
- data/assets/MacGap.app/Contents/Resources/_http_outgoing.js +597 -0
- data/assets/MacGap.app/Contents/Resources/_http_server.js +510 -0
- data/assets/MacGap.app/Contents/Resources/_linklist.js +76 -0
- data/assets/MacGap.app/Contents/Resources/_stream_duplex.js +69 -0
- data/assets/MacGap.app/Contents/Resources/_stream_passthrough.js +41 -0
- data/assets/MacGap.app/Contents/Resources/_stream_readable.js +900 -0
- data/assets/MacGap.app/Contents/Resources/_stream_transform.js +204 -0
- data/assets/MacGap.app/Contents/Resources/_stream_writable.js +456 -0
- data/assets/MacGap.app/Contents/Resources/_tls_legacy.js +887 -0
- data/assets/MacGap.app/Contents/Resources/_tls_wrap.js +831 -0
- data/assets/MacGap.app/Contents/Resources/application.icns +0 -0
- data/assets/MacGap.app/Contents/Resources/assert.js +326 -0
- data/assets/MacGap.app/Contents/Resources/buffer.js +724 -0
- data/assets/MacGap.app/Contents/Resources/child_process.js +1107 -0
- data/assets/MacGap.app/Contents/Resources/cluster.js +613 -0
- data/assets/MacGap.app/Contents/Resources/console.js +108 -0
- data/assets/MacGap.app/Contents/Resources/constants.js +22 -0
- data/assets/MacGap.app/Contents/Resources/crypto.js +691 -0
- data/assets/MacGap.app/Contents/Resources/dgram.js +459 -0
- data/assets/MacGap.app/Contents/Resources/dns.js +274 -0
- data/assets/MacGap.app/Contents/Resources/domain.js +292 -0
- data/assets/MacGap.app/Contents/Resources/en.lproj/Credits.rtf +29 -0
- data/assets/MacGap.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
- data/assets/MacGap.app/Contents/Resources/en.lproj/MainMenu.nib +0 -0
- data/assets/MacGap.app/Contents/Resources/en.lproj/Window.nib +0 -0
- data/assets/MacGap.app/Contents/Resources/events.js +312 -0
- data/assets/MacGap.app/Contents/Resources/freelist.js +43 -0
- data/assets/MacGap.app/Contents/Resources/fs.js +1732 -0
- data/assets/MacGap.app/Contents/Resources/http.js +119 -0
- data/assets/MacGap.app/Contents/Resources/https.js +134 -0
- data/assets/MacGap.app/Contents/Resources/module.js +529 -0
- data/assets/MacGap.app/Contents/Resources/net.js +1378 -0
- data/assets/MacGap.app/Contents/Resources/nodelike.js +195 -0
- data/assets/MacGap.app/Contents/Resources/os.js +64 -0
- data/assets/MacGap.app/Contents/Resources/path.js +517 -0
- data/assets/MacGap.app/Contents/Resources/public/index.html +38 -0
- data/assets/MacGap.app/Contents/Resources/punycode.js +507 -0
- data/assets/MacGap.app/Contents/Resources/querystring.js +206 -0
- data/assets/MacGap.app/Contents/Resources/readline.js +1311 -0
- data/assets/MacGap.app/Contents/Resources/repl.js +945 -0
- data/assets/MacGap.app/Contents/Resources/smalloc.js +90 -0
- data/assets/MacGap.app/Contents/Resources/stream.js +127 -0
- data/assets/MacGap.app/Contents/Resources/string_decoder.js +189 -0
- data/assets/MacGap.app/Contents/Resources/sys.js +24 -0
- data/assets/MacGap.app/Contents/Resources/timers.js +568 -0
- data/assets/MacGap.app/Contents/Resources/tls.js +220 -0
- data/assets/MacGap.app/Contents/Resources/tty.js +129 -0
- data/assets/MacGap.app/Contents/Resources/url.js +693 -0
- data/assets/MacGap.app/Contents/Resources/util.js +688 -0
- data/assets/MacGap.app/Contents/Resources/vm.js +73 -0
- data/assets/MacGap.app/Contents/Resources/zlib.js +524 -0
- data/assets/index.html +38 -0
- data/bin/macgyver +104 -0
- data/macgyver.gemspec +19 -0
- data/test/public/index.html +27 -0
- metadata +121 -0
@@ -0,0 +1,206 @@
|
|
1
|
+
// Copyright Joyent, Inc. and other Node contributors.
|
2
|
+
//
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a
|
4
|
+
// copy of this software and associated documentation files (the
|
5
|
+
// "Software"), to deal in the Software without restriction, including
|
6
|
+
// without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
8
|
+
// persons to whom the Software is furnished to do so, subject to the
|
9
|
+
// following conditions:
|
10
|
+
//
|
11
|
+
// The above copyright notice and this permission notice shall be included
|
12
|
+
// in all copies or substantial portions of the Software.
|
13
|
+
//
|
14
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
15
|
+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
17
|
+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
18
|
+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
19
|
+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
20
|
+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
// Query String Utilities
|
23
|
+
|
24
|
+
var QueryString = exports;
|
25
|
+
var util = require('util');
|
26
|
+
|
27
|
+
|
28
|
+
// If obj.hasOwnProperty has been overridden, then calling
|
29
|
+
// obj.hasOwnProperty(prop) will break.
|
30
|
+
// See: https://github.com/joyent/node/issues/1707
|
31
|
+
function hasOwnProperty(obj, prop) {
|
32
|
+
return Object.prototype.hasOwnProperty.call(obj, prop);
|
33
|
+
}
|
34
|
+
|
35
|
+
|
36
|
+
function charCode(c) {
|
37
|
+
return c.charCodeAt(0);
|
38
|
+
}
|
39
|
+
|
40
|
+
|
41
|
+
// a safe fast alternative to decodeURIComponent
|
42
|
+
QueryString.unescapeBuffer = function(s, decodeSpaces) {
|
43
|
+
var out = new Buffer(s.length);
|
44
|
+
var state = 'CHAR'; // states: CHAR, HEX0, HEX1
|
45
|
+
var n, m, hexchar;
|
46
|
+
|
47
|
+
for (var inIndex = 0, outIndex = 0; inIndex <= s.length; inIndex++) {
|
48
|
+
var c = s.charCodeAt(inIndex);
|
49
|
+
switch (state) {
|
50
|
+
case 'CHAR':
|
51
|
+
switch (c) {
|
52
|
+
case charCode('%'):
|
53
|
+
n = 0;
|
54
|
+
m = 0;
|
55
|
+
state = 'HEX0';
|
56
|
+
break;
|
57
|
+
case charCode('+'):
|
58
|
+
if (decodeSpaces) c = charCode(' ');
|
59
|
+
// pass thru
|
60
|
+
default:
|
61
|
+
out[outIndex++] = c;
|
62
|
+
break;
|
63
|
+
}
|
64
|
+
break;
|
65
|
+
|
66
|
+
case 'HEX0':
|
67
|
+
state = 'HEX1';
|
68
|
+
hexchar = c;
|
69
|
+
if (charCode('0') <= c && c <= charCode('9')) {
|
70
|
+
n = c - charCode('0');
|
71
|
+
} else if (charCode('a') <= c && c <= charCode('f')) {
|
72
|
+
n = c - charCode('a') + 10;
|
73
|
+
} else if (charCode('A') <= c && c <= charCode('F')) {
|
74
|
+
n = c - charCode('A') + 10;
|
75
|
+
} else {
|
76
|
+
out[outIndex++] = charCode('%');
|
77
|
+
out[outIndex++] = c;
|
78
|
+
state = 'CHAR';
|
79
|
+
break;
|
80
|
+
}
|
81
|
+
break;
|
82
|
+
|
83
|
+
case 'HEX1':
|
84
|
+
state = 'CHAR';
|
85
|
+
if (charCode('0') <= c && c <= charCode('9')) {
|
86
|
+
m = c - charCode('0');
|
87
|
+
} else if (charCode('a') <= c && c <= charCode('f')) {
|
88
|
+
m = c - charCode('a') + 10;
|
89
|
+
} else if (charCode('A') <= c && c <= charCode('F')) {
|
90
|
+
m = c - charCode('A') + 10;
|
91
|
+
} else {
|
92
|
+
out[outIndex++] = charCode('%');
|
93
|
+
out[outIndex++] = hexchar;
|
94
|
+
out[outIndex++] = c;
|
95
|
+
break;
|
96
|
+
}
|
97
|
+
out[outIndex++] = 16 * n + m;
|
98
|
+
break;
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
// TODO support returning arbitrary buffers.
|
103
|
+
|
104
|
+
return out.slice(0, outIndex - 1);
|
105
|
+
};
|
106
|
+
|
107
|
+
|
108
|
+
QueryString.unescape = function(s, decodeSpaces) {
|
109
|
+
return QueryString.unescapeBuffer(s, decodeSpaces).toString();
|
110
|
+
};
|
111
|
+
|
112
|
+
|
113
|
+
QueryString.escape = function(str) {
|
114
|
+
return encodeURIComponent(str);
|
115
|
+
};
|
116
|
+
|
117
|
+
var stringifyPrimitive = function(v) {
|
118
|
+
if (util.isString(v))
|
119
|
+
return v;
|
120
|
+
if (util.isBoolean(v))
|
121
|
+
return v ? 'true' : 'false';
|
122
|
+
if (util.isNumber(v))
|
123
|
+
return isFinite(v) ? v : '';
|
124
|
+
return '';
|
125
|
+
};
|
126
|
+
|
127
|
+
|
128
|
+
QueryString.stringify = QueryString.encode = function(obj, sep, eq) {
|
129
|
+
sep = sep || '&';
|
130
|
+
eq = eq || '=';
|
131
|
+
if (util.isNull(obj)) {
|
132
|
+
obj = undefined;
|
133
|
+
}
|
134
|
+
|
135
|
+
if (util.isObject(obj)) {
|
136
|
+
return Object.keys(obj).map(function(k) {
|
137
|
+
var ks = QueryString.escape(stringifyPrimitive(k)) + eq;
|
138
|
+
if (util.isArray(obj[k])) {
|
139
|
+
return obj[k].map(function(v) {
|
140
|
+
return ks + QueryString.escape(stringifyPrimitive(v));
|
141
|
+
}).join(sep);
|
142
|
+
} else {
|
143
|
+
return ks + QueryString.escape(stringifyPrimitive(obj[k]));
|
144
|
+
}
|
145
|
+
}).join(sep);
|
146
|
+
|
147
|
+
}
|
148
|
+
return '';
|
149
|
+
};
|
150
|
+
|
151
|
+
// Parse a key=val string.
|
152
|
+
QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
|
153
|
+
sep = sep || '&';
|
154
|
+
eq = eq || '=';
|
155
|
+
var obj = {};
|
156
|
+
|
157
|
+
if (!util.isString(qs) || qs.length === 0) {
|
158
|
+
return obj;
|
159
|
+
}
|
160
|
+
|
161
|
+
var regexp = /\+/g;
|
162
|
+
qs = qs.split(sep);
|
163
|
+
|
164
|
+
var maxKeys = 1000;
|
165
|
+
if (options && util.isNumber(options.maxKeys)) {
|
166
|
+
maxKeys = options.maxKeys;
|
167
|
+
}
|
168
|
+
|
169
|
+
var len = qs.length;
|
170
|
+
// maxKeys <= 0 means that we should not limit keys count
|
171
|
+
if (maxKeys > 0 && len > maxKeys) {
|
172
|
+
len = maxKeys;
|
173
|
+
}
|
174
|
+
|
175
|
+
for (var i = 0; i < len; ++i) {
|
176
|
+
var x = qs[i].replace(regexp, '%20'),
|
177
|
+
idx = x.indexOf(eq),
|
178
|
+
kstr, vstr, k, v;
|
179
|
+
|
180
|
+
if (idx >= 0) {
|
181
|
+
kstr = x.substr(0, idx);
|
182
|
+
vstr = x.substr(idx + 1);
|
183
|
+
} else {
|
184
|
+
kstr = x;
|
185
|
+
vstr = '';
|
186
|
+
}
|
187
|
+
|
188
|
+
try {
|
189
|
+
k = decodeURIComponent(kstr);
|
190
|
+
v = decodeURIComponent(vstr);
|
191
|
+
} catch (e) {
|
192
|
+
k = QueryString.unescape(kstr, true);
|
193
|
+
v = QueryString.unescape(vstr, true);
|
194
|
+
}
|
195
|
+
|
196
|
+
if (!hasOwnProperty(obj, k)) {
|
197
|
+
obj[k] = v;
|
198
|
+
} else if (util.isArray(obj[k])) {
|
199
|
+
obj[k].push(v);
|
200
|
+
} else {
|
201
|
+
obj[k] = [obj[k], v];
|
202
|
+
}
|
203
|
+
}
|
204
|
+
|
205
|
+
return obj;
|
206
|
+
};
|
@@ -0,0 +1,1311 @@
|
|
1
|
+
// Copyright Joyent, Inc. and other Node contributors.
|
2
|
+
//
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a
|
4
|
+
// copy of this software and associated documentation files (the
|
5
|
+
// "Software"), to deal in the Software without restriction, including
|
6
|
+
// without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
8
|
+
// persons to whom the Software is furnished to do so, subject to the
|
9
|
+
// following conditions:
|
10
|
+
//
|
11
|
+
// The above copyright notice and this permission notice shall be included
|
12
|
+
// in all copies or substantial portions of the Software.
|
13
|
+
//
|
14
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
15
|
+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
17
|
+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
18
|
+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
19
|
+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
20
|
+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
// Inspiration for this code comes from Salvatore Sanfilippo's linenoise.
|
23
|
+
// https://github.com/antirez/linenoise
|
24
|
+
// Reference:
|
25
|
+
// * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
26
|
+
// * http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
|
27
|
+
|
28
|
+
var kHistorySize = 30;
|
29
|
+
|
30
|
+
var util = require('util');
|
31
|
+
var inherits = require('util').inherits;
|
32
|
+
var EventEmitter = require('events').EventEmitter;
|
33
|
+
|
34
|
+
|
35
|
+
exports.createInterface = function(input, output, completer, terminal) {
|
36
|
+
var rl;
|
37
|
+
if (arguments.length === 1) {
|
38
|
+
rl = new Interface(input);
|
39
|
+
} else {
|
40
|
+
rl = new Interface(input, output, completer, terminal);
|
41
|
+
}
|
42
|
+
return rl;
|
43
|
+
};
|
44
|
+
|
45
|
+
|
46
|
+
function Interface(input, output, completer, terminal) {
|
47
|
+
if (!(this instanceof Interface)) {
|
48
|
+
return new Interface(input, output, completer, terminal);
|
49
|
+
}
|
50
|
+
|
51
|
+
this._sawReturn = false;
|
52
|
+
|
53
|
+
EventEmitter.call(this);
|
54
|
+
|
55
|
+
if (arguments.length === 1) {
|
56
|
+
// an options object was given
|
57
|
+
output = input.output;
|
58
|
+
completer = input.completer;
|
59
|
+
terminal = input.terminal;
|
60
|
+
input = input.input;
|
61
|
+
}
|
62
|
+
|
63
|
+
completer = completer || function() { return []; };
|
64
|
+
|
65
|
+
if (!util.isFunction(completer)) {
|
66
|
+
throw new TypeError('Argument \'completer\' must be a function');
|
67
|
+
}
|
68
|
+
|
69
|
+
// backwards compat; check the isTTY prop of the output stream
|
70
|
+
// when `terminal` was not specified
|
71
|
+
if (util.isUndefined(terminal)) {
|
72
|
+
terminal = !!output.isTTY;
|
73
|
+
}
|
74
|
+
|
75
|
+
var self = this;
|
76
|
+
|
77
|
+
this.output = output;
|
78
|
+
this.input = input;
|
79
|
+
|
80
|
+
// Check arity, 2 - for async, 1 for sync
|
81
|
+
this.completer = completer.length === 2 ? completer : function(v, callback) {
|
82
|
+
callback(null, completer(v));
|
83
|
+
};
|
84
|
+
|
85
|
+
this.setPrompt('> ');
|
86
|
+
|
87
|
+
this.terminal = !!terminal;
|
88
|
+
|
89
|
+
function ondata(data) {
|
90
|
+
self._normalWrite(data);
|
91
|
+
}
|
92
|
+
|
93
|
+
function onend() {
|
94
|
+
self.close();
|
95
|
+
}
|
96
|
+
|
97
|
+
function onkeypress(s, key) {
|
98
|
+
self._ttyWrite(s, key);
|
99
|
+
}
|
100
|
+
|
101
|
+
function onresize() {
|
102
|
+
self._refreshLine();
|
103
|
+
}
|
104
|
+
|
105
|
+
if (!this.terminal) {
|
106
|
+
input.on('data', ondata);
|
107
|
+
input.on('end', onend);
|
108
|
+
self.once('close', function() {
|
109
|
+
input.removeListener('data', ondata);
|
110
|
+
input.removeListener('end', onend);
|
111
|
+
});
|
112
|
+
var StringDecoder = require('string_decoder').StringDecoder; // lazy load
|
113
|
+
this._decoder = new StringDecoder('utf8');
|
114
|
+
|
115
|
+
} else {
|
116
|
+
|
117
|
+
exports.emitKeypressEvents(input);
|
118
|
+
|
119
|
+
// input usually refers to stdin
|
120
|
+
input.on('keypress', onkeypress);
|
121
|
+
|
122
|
+
// Current line
|
123
|
+
this.line = '';
|
124
|
+
|
125
|
+
this._setRawMode(true);
|
126
|
+
this.terminal = true;
|
127
|
+
|
128
|
+
// Cursor position on the line.
|
129
|
+
this.cursor = 0;
|
130
|
+
|
131
|
+
this.history = [];
|
132
|
+
this.historyIndex = -1;
|
133
|
+
|
134
|
+
output.on('resize', onresize);
|
135
|
+
self.once('close', function() {
|
136
|
+
input.removeListener('keypress', onkeypress);
|
137
|
+
output.removeListener('resize', onresize);
|
138
|
+
});
|
139
|
+
}
|
140
|
+
|
141
|
+
input.resume();
|
142
|
+
}
|
143
|
+
|
144
|
+
inherits(Interface, EventEmitter);
|
145
|
+
|
146
|
+
Interface.prototype.__defineGetter__('columns', function() {
|
147
|
+
return this.output.columns || Infinity;
|
148
|
+
});
|
149
|
+
|
150
|
+
Interface.prototype.setPrompt = function(prompt) {
|
151
|
+
this._prompt = prompt;
|
152
|
+
};
|
153
|
+
|
154
|
+
|
155
|
+
Interface.prototype._setRawMode = function(mode) {
|
156
|
+
if (util.isFunction(this.input.setRawMode)) {
|
157
|
+
return this.input.setRawMode(mode);
|
158
|
+
}
|
159
|
+
};
|
160
|
+
|
161
|
+
|
162
|
+
Interface.prototype.prompt = function(preserveCursor) {
|
163
|
+
if (this.paused) this.resume();
|
164
|
+
if (this.terminal) {
|
165
|
+
if (!preserveCursor) this.cursor = 0;
|
166
|
+
this._refreshLine();
|
167
|
+
} else {
|
168
|
+
this.output.write(this._prompt);
|
169
|
+
}
|
170
|
+
};
|
171
|
+
|
172
|
+
|
173
|
+
Interface.prototype.question = function(query, cb) {
|
174
|
+
if (util.isFunction(cb)) {
|
175
|
+
if (this._questionCallback) {
|
176
|
+
this.prompt();
|
177
|
+
} else {
|
178
|
+
this._oldPrompt = this._prompt;
|
179
|
+
this.setPrompt(query);
|
180
|
+
this._questionCallback = cb;
|
181
|
+
this.prompt();
|
182
|
+
}
|
183
|
+
}
|
184
|
+
};
|
185
|
+
|
186
|
+
|
187
|
+
Interface.prototype._onLine = function(line) {
|
188
|
+
if (this._questionCallback) {
|
189
|
+
var cb = this._questionCallback;
|
190
|
+
this._questionCallback = null;
|
191
|
+
this.setPrompt(this._oldPrompt);
|
192
|
+
cb(line);
|
193
|
+
} else {
|
194
|
+
this.emit('line', line);
|
195
|
+
}
|
196
|
+
};
|
197
|
+
|
198
|
+
|
199
|
+
Interface.prototype._addHistory = function() {
|
200
|
+
if (this.line.length === 0) return '';
|
201
|
+
|
202
|
+
if (this.history.length === 0 || this.history[0] !== this.line) {
|
203
|
+
this.history.unshift(this.line);
|
204
|
+
|
205
|
+
// Only store so many
|
206
|
+
if (this.history.length > kHistorySize) this.history.pop();
|
207
|
+
}
|
208
|
+
|
209
|
+
this.historyIndex = -1;
|
210
|
+
return this.history[0];
|
211
|
+
};
|
212
|
+
|
213
|
+
|
214
|
+
Interface.prototype._refreshLine = function() {
|
215
|
+
// line length
|
216
|
+
var line = this._prompt + this.line;
|
217
|
+
var dispPos = this._getDisplayPos(line);
|
218
|
+
var lineCols = dispPos.cols;
|
219
|
+
var lineRows = dispPos.rows;
|
220
|
+
|
221
|
+
// cursor position
|
222
|
+
var cursorPos = this._getCursorPos();
|
223
|
+
|
224
|
+
// first move to the bottom of the current line, based on cursor pos
|
225
|
+
var prevRows = this.prevRows || 0;
|
226
|
+
if (prevRows > 0) {
|
227
|
+
exports.moveCursor(this.output, 0, -prevRows);
|
228
|
+
}
|
229
|
+
|
230
|
+
// Cursor to left edge.
|
231
|
+
exports.cursorTo(this.output, 0);
|
232
|
+
// erase data
|
233
|
+
exports.clearScreenDown(this.output);
|
234
|
+
|
235
|
+
// Write the prompt and the current buffer content.
|
236
|
+
this.output.write(line);
|
237
|
+
|
238
|
+
// Force terminal to allocate a new line
|
239
|
+
if (lineCols === 0) {
|
240
|
+
this.output.write(' ');
|
241
|
+
}
|
242
|
+
|
243
|
+
// Move cursor to original position.
|
244
|
+
exports.cursorTo(this.output, cursorPos.cols);
|
245
|
+
|
246
|
+
var diff = lineRows - cursorPos.rows;
|
247
|
+
if (diff > 0) {
|
248
|
+
exports.moveCursor(this.output, 0, -diff);
|
249
|
+
}
|
250
|
+
|
251
|
+
this.prevRows = cursorPos.rows;
|
252
|
+
};
|
253
|
+
|
254
|
+
|
255
|
+
Interface.prototype.close = function() {
|
256
|
+
if (this.closed) return;
|
257
|
+
this.pause();
|
258
|
+
if (this.terminal) {
|
259
|
+
this._setRawMode(false);
|
260
|
+
}
|
261
|
+
this.closed = true;
|
262
|
+
this.emit('close');
|
263
|
+
};
|
264
|
+
|
265
|
+
|
266
|
+
Interface.prototype.pause = function() {
|
267
|
+
if (this.paused) return;
|
268
|
+
this.input.pause();
|
269
|
+
this.paused = true;
|
270
|
+
this.emit('pause');
|
271
|
+
return this;
|
272
|
+
};
|
273
|
+
|
274
|
+
|
275
|
+
Interface.prototype.resume = function() {
|
276
|
+
if (!this.paused) return;
|
277
|
+
this.input.resume();
|
278
|
+
this.paused = false;
|
279
|
+
this.emit('resume');
|
280
|
+
return this;
|
281
|
+
};
|
282
|
+
|
283
|
+
|
284
|
+
Interface.prototype.write = function(d, key) {
|
285
|
+
if (this.paused) this.resume();
|
286
|
+
this.terminal ? this._ttyWrite(d, key) : this._normalWrite(d);
|
287
|
+
};
|
288
|
+
|
289
|
+
// \r\n, \n, or \r followed by something other than \n
|
290
|
+
var lineEnding = /\r?\n|\r(?!\n)/;
|
291
|
+
Interface.prototype._normalWrite = function(b) {
|
292
|
+
if (util.isUndefined(b)) {
|
293
|
+
return;
|
294
|
+
}
|
295
|
+
var string = this._decoder.write(b);
|
296
|
+
if (this._sawReturn) {
|
297
|
+
string = string.replace(/^\n/, '');
|
298
|
+
this._sawReturn = false;
|
299
|
+
}
|
300
|
+
|
301
|
+
if (this._line_buffer) {
|
302
|
+
string = this._line_buffer + string;
|
303
|
+
this._line_buffer = null;
|
304
|
+
}
|
305
|
+
if (lineEnding.test(string)) {
|
306
|
+
this._sawReturn = /\r$/.test(string);
|
307
|
+
|
308
|
+
// got one or more newlines; process into "line" events
|
309
|
+
var lines = string.split(lineEnding);
|
310
|
+
// either '' or (concievably) the unfinished portion of the next line
|
311
|
+
string = lines.pop();
|
312
|
+
this._line_buffer = string;
|
313
|
+
lines.forEach(function(line) {
|
314
|
+
this._onLine(line);
|
315
|
+
}, this);
|
316
|
+
} else if (string) {
|
317
|
+
// no newlines this time, save what we have for next time
|
318
|
+
this._line_buffer = string;
|
319
|
+
}
|
320
|
+
};
|
321
|
+
|
322
|
+
Interface.prototype._insertString = function(c) {
|
323
|
+
//BUG: Problem when adding tabs with following content.
|
324
|
+
// Perhaps the bug is in _refreshLine(). Not sure.
|
325
|
+
// A hack would be to insert spaces instead of literal '\t'.
|
326
|
+
if (this.cursor < this.line.length) {
|
327
|
+
var beg = this.line.slice(0, this.cursor);
|
328
|
+
var end = this.line.slice(this.cursor, this.line.length);
|
329
|
+
this.line = beg + c + end;
|
330
|
+
this.cursor += c.length;
|
331
|
+
this._refreshLine();
|
332
|
+
} else {
|
333
|
+
this.line += c;
|
334
|
+
this.cursor += c.length;
|
335
|
+
|
336
|
+
if (this._getCursorPos().cols === 0) {
|
337
|
+
this._refreshLine();
|
338
|
+
} else {
|
339
|
+
this.output.write(c);
|
340
|
+
}
|
341
|
+
|
342
|
+
// a hack to get the line refreshed if it's needed
|
343
|
+
this._moveCursor(0);
|
344
|
+
}
|
345
|
+
};
|
346
|
+
|
347
|
+
Interface.prototype._tabComplete = function() {
|
348
|
+
var self = this;
|
349
|
+
|
350
|
+
self.pause();
|
351
|
+
self.completer(self.line.slice(0, self.cursor), function(err, rv) {
|
352
|
+
self.resume();
|
353
|
+
|
354
|
+
if (err) {
|
355
|
+
// XXX Log it somewhere?
|
356
|
+
return;
|
357
|
+
}
|
358
|
+
|
359
|
+
var completions = rv[0],
|
360
|
+
completeOn = rv[1]; // the text that was completed
|
361
|
+
if (completions && completions.length) {
|
362
|
+
// Apply/show completions.
|
363
|
+
if (completions.length === 1) {
|
364
|
+
self._insertString(completions[0].slice(completeOn.length));
|
365
|
+
} else {
|
366
|
+
self.output.write('\r\n');
|
367
|
+
var width = completions.reduce(function(a, b) {
|
368
|
+
return a.length > b.length ? a : b;
|
369
|
+
}).length + 2; // 2 space padding
|
370
|
+
var maxColumns = Math.floor(self.columns / width) || 1;
|
371
|
+
var group = [], c;
|
372
|
+
for (var i = 0, compLen = completions.length; i < compLen; i++) {
|
373
|
+
c = completions[i];
|
374
|
+
if (c === '') {
|
375
|
+
handleGroup(self, group, width, maxColumns);
|
376
|
+
group = [];
|
377
|
+
} else {
|
378
|
+
group.push(c);
|
379
|
+
}
|
380
|
+
}
|
381
|
+
handleGroup(self, group, width, maxColumns);
|
382
|
+
|
383
|
+
// If there is a common prefix to all matches, then apply that
|
384
|
+
// portion.
|
385
|
+
var f = completions.filter(function(e) { if (e) return e; });
|
386
|
+
var prefix = commonPrefix(f);
|
387
|
+
if (prefix.length > completeOn.length) {
|
388
|
+
self._insertString(prefix.slice(completeOn.length));
|
389
|
+
}
|
390
|
+
|
391
|
+
}
|
392
|
+
self._refreshLine();
|
393
|
+
}
|
394
|
+
});
|
395
|
+
};
|
396
|
+
|
397
|
+
// this = Interface instance
|
398
|
+
function handleGroup(self, group, width, maxColumns) {
|
399
|
+
if (group.length == 0) {
|
400
|
+
return;
|
401
|
+
}
|
402
|
+
var minRows = Math.ceil(group.length / maxColumns);
|
403
|
+
for (var row = 0; row < minRows; row++) {
|
404
|
+
for (var col = 0; col < maxColumns; col++) {
|
405
|
+
var idx = row * maxColumns + col;
|
406
|
+
if (idx >= group.length) {
|
407
|
+
break;
|
408
|
+
}
|
409
|
+
var item = group[idx];
|
410
|
+
self.output.write(item);
|
411
|
+
if (col < maxColumns - 1) {
|
412
|
+
for (var s = 0, itemLen = item.length; s < width - itemLen;
|
413
|
+
s++) {
|
414
|
+
self.output.write(' ');
|
415
|
+
}
|
416
|
+
}
|
417
|
+
}
|
418
|
+
self.output.write('\r\n');
|
419
|
+
}
|
420
|
+
self.output.write('\r\n');
|
421
|
+
}
|
422
|
+
|
423
|
+
function commonPrefix(strings) {
|
424
|
+
if (!strings || strings.length == 0) {
|
425
|
+
return '';
|
426
|
+
}
|
427
|
+
var sorted = strings.slice().sort();
|
428
|
+
var min = sorted[0];
|
429
|
+
var max = sorted[sorted.length - 1];
|
430
|
+
for (var i = 0, len = min.length; i < len; i++) {
|
431
|
+
if (min[i] != max[i]) {
|
432
|
+
return min.slice(0, i);
|
433
|
+
}
|
434
|
+
}
|
435
|
+
return min;
|
436
|
+
}
|
437
|
+
|
438
|
+
|
439
|
+
Interface.prototype._wordLeft = function() {
|
440
|
+
if (this.cursor > 0) {
|
441
|
+
var leading = this.line.slice(0, this.cursor);
|
442
|
+
var match = leading.match(/([^\w\s]+|\w+|)\s*$/);
|
443
|
+
this._moveCursor(-match[0].length);
|
444
|
+
}
|
445
|
+
};
|
446
|
+
|
447
|
+
|
448
|
+
Interface.prototype._wordRight = function() {
|
449
|
+
if (this.cursor < this.line.length) {
|
450
|
+
var trailing = this.line.slice(this.cursor);
|
451
|
+
var match = trailing.match(/^(\s+|\W+|\w+)\s*/);
|
452
|
+
this._moveCursor(match[0].length);
|
453
|
+
}
|
454
|
+
};
|
455
|
+
|
456
|
+
|
457
|
+
Interface.prototype._deleteLeft = function() {
|
458
|
+
if (this.cursor > 0 && this.line.length > 0) {
|
459
|
+
this.line = this.line.slice(0, this.cursor - 1) +
|
460
|
+
this.line.slice(this.cursor, this.line.length);
|
461
|
+
|
462
|
+
this.cursor--;
|
463
|
+
this._refreshLine();
|
464
|
+
}
|
465
|
+
};
|
466
|
+
|
467
|
+
|
468
|
+
Interface.prototype._deleteRight = function() {
|
469
|
+
this.line = this.line.slice(0, this.cursor) +
|
470
|
+
this.line.slice(this.cursor + 1, this.line.length);
|
471
|
+
this._refreshLine();
|
472
|
+
};
|
473
|
+
|
474
|
+
|
475
|
+
Interface.prototype._deleteWordLeft = function() {
|
476
|
+
if (this.cursor > 0) {
|
477
|
+
var leading = this.line.slice(0, this.cursor);
|
478
|
+
var match = leading.match(/([^\w\s]+|\w+|)\s*$/);
|
479
|
+
leading = leading.slice(0, leading.length - match[0].length);
|
480
|
+
this.line = leading + this.line.slice(this.cursor, this.line.length);
|
481
|
+
this.cursor = leading.length;
|
482
|
+
this._refreshLine();
|
483
|
+
}
|
484
|
+
};
|
485
|
+
|
486
|
+
|
487
|
+
Interface.prototype._deleteWordRight = function() {
|
488
|
+
if (this.cursor < this.line.length) {
|
489
|
+
var trailing = this.line.slice(this.cursor);
|
490
|
+
var match = trailing.match(/^(\s+|\W+|\w+)\s*/);
|
491
|
+
this.line = this.line.slice(0, this.cursor) +
|
492
|
+
trailing.slice(match[0].length);
|
493
|
+
this._refreshLine();
|
494
|
+
}
|
495
|
+
};
|
496
|
+
|
497
|
+
|
498
|
+
Interface.prototype._deleteLineLeft = function() {
|
499
|
+
this.line = this.line.slice(this.cursor);
|
500
|
+
this.cursor = 0;
|
501
|
+
this._refreshLine();
|
502
|
+
};
|
503
|
+
|
504
|
+
|
505
|
+
Interface.prototype._deleteLineRight = function() {
|
506
|
+
this.line = this.line.slice(0, this.cursor);
|
507
|
+
this._refreshLine();
|
508
|
+
};
|
509
|
+
|
510
|
+
|
511
|
+
Interface.prototype.clearLine = function() {
|
512
|
+
this._moveCursor(+Infinity);
|
513
|
+
this.output.write('\r\n');
|
514
|
+
this.line = '';
|
515
|
+
this.cursor = 0;
|
516
|
+
this.prevRows = 0;
|
517
|
+
};
|
518
|
+
|
519
|
+
|
520
|
+
Interface.prototype._line = function() {
|
521
|
+
var line = this._addHistory();
|
522
|
+
this.clearLine();
|
523
|
+
this._onLine(line);
|
524
|
+
};
|
525
|
+
|
526
|
+
|
527
|
+
Interface.prototype._historyNext = function() {
|
528
|
+
if (this.historyIndex > 0) {
|
529
|
+
this.historyIndex--;
|
530
|
+
this.line = this.history[this.historyIndex];
|
531
|
+
this.cursor = this.line.length; // set cursor to end of line.
|
532
|
+
this._refreshLine();
|
533
|
+
|
534
|
+
} else if (this.historyIndex === 0) {
|
535
|
+
this.historyIndex = -1;
|
536
|
+
this.cursor = 0;
|
537
|
+
this.line = '';
|
538
|
+
this._refreshLine();
|
539
|
+
}
|
540
|
+
};
|
541
|
+
|
542
|
+
|
543
|
+
Interface.prototype._historyPrev = function() {
|
544
|
+
if (this.historyIndex + 1 < this.history.length) {
|
545
|
+
this.historyIndex++;
|
546
|
+
this.line = this.history[this.historyIndex];
|
547
|
+
this.cursor = this.line.length; // set cursor to end of line.
|
548
|
+
|
549
|
+
this._refreshLine();
|
550
|
+
}
|
551
|
+
};
|
552
|
+
|
553
|
+
|
554
|
+
// Returns the last character's display position of the given string
|
555
|
+
Interface.prototype._getDisplayPos = function(str) {
|
556
|
+
var offset = 0;
|
557
|
+
var col = this.columns;
|
558
|
+
var code;
|
559
|
+
str = stripVTControlCharacters(str);
|
560
|
+
for (var i = 0, len = str.length; i < len; i++) {
|
561
|
+
code = codePointAt(str, i);
|
562
|
+
if (code >= 0x10000) { // surrogates
|
563
|
+
i++;
|
564
|
+
}
|
565
|
+
if (isFullWidthCodePoint(code)) {
|
566
|
+
if ((offset + 1) % col === 0) {
|
567
|
+
offset++;
|
568
|
+
}
|
569
|
+
offset += 2;
|
570
|
+
} else {
|
571
|
+
offset++;
|
572
|
+
}
|
573
|
+
}
|
574
|
+
var cols = offset % col;
|
575
|
+
var rows = (offset - cols) / col;
|
576
|
+
return {cols: cols, rows: rows};
|
577
|
+
};
|
578
|
+
|
579
|
+
|
580
|
+
// Returns current cursor's position and line
|
581
|
+
Interface.prototype._getCursorPos = function() {
|
582
|
+
var columns = this.columns;
|
583
|
+
var strBeforeCursor = this._prompt + this.line.substring(0, this.cursor);
|
584
|
+
var dispPos = this._getDisplayPos(stripVTControlCharacters(strBeforeCursor));
|
585
|
+
var cols = dispPos.cols;
|
586
|
+
var rows = dispPos.rows;
|
587
|
+
// If the cursor is on a full-width character which steps over the line,
|
588
|
+
// move the cursor to the beginning of the next line.
|
589
|
+
if (cols + 1 === columns &&
|
590
|
+
this.cursor < this.line.length &&
|
591
|
+
isFullWidthCodePoint(codePointAt(this.line, this.cursor))) {
|
592
|
+
rows++;
|
593
|
+
cols = 0;
|
594
|
+
}
|
595
|
+
return {cols: cols, rows: rows};
|
596
|
+
};
|
597
|
+
|
598
|
+
|
599
|
+
// This function moves cursor dx places to the right
|
600
|
+
// (-dx for left) and refreshes the line if it is needed
|
601
|
+
Interface.prototype._moveCursor = function(dx) {
|
602
|
+
var oldcursor = this.cursor;
|
603
|
+
var oldPos = this._getCursorPos();
|
604
|
+
this.cursor += dx;
|
605
|
+
|
606
|
+
// bounds check
|
607
|
+
if (this.cursor < 0) this.cursor = 0;
|
608
|
+
else if (this.cursor > this.line.length) this.cursor = this.line.length;
|
609
|
+
|
610
|
+
var newPos = this._getCursorPos();
|
611
|
+
|
612
|
+
// check if cursors are in the same line
|
613
|
+
if (oldPos.rows === newPos.rows) {
|
614
|
+
var diffCursor = this.cursor - oldcursor;
|
615
|
+
var diffWidth;
|
616
|
+
if (diffCursor < 0) {
|
617
|
+
diffWidth = -getStringWidth(
|
618
|
+
this.line.substring(this.cursor, oldcursor)
|
619
|
+
);
|
620
|
+
} else if (diffCursor > 0) {
|
621
|
+
diffWidth = getStringWidth(
|
622
|
+
this.line.substring(this.cursor, oldcursor)
|
623
|
+
);
|
624
|
+
}
|
625
|
+
exports.moveCursor(this.output, diffWidth, 0);
|
626
|
+
this.prevRows = newPos.rows;
|
627
|
+
} else {
|
628
|
+
this._refreshLine();
|
629
|
+
}
|
630
|
+
};
|
631
|
+
|
632
|
+
|
633
|
+
// handle a write from the tty
|
634
|
+
Interface.prototype._ttyWrite = function(s, key) {
|
635
|
+
key = key || {};
|
636
|
+
|
637
|
+
// Ignore escape key - Fixes #2876
|
638
|
+
if (key.name == 'escape') return;
|
639
|
+
|
640
|
+
if (key.ctrl && key.shift) {
|
641
|
+
/* Control and shift pressed */
|
642
|
+
switch (key.name) {
|
643
|
+
case 'backspace':
|
644
|
+
this._deleteLineLeft();
|
645
|
+
break;
|
646
|
+
|
647
|
+
case 'delete':
|
648
|
+
this._deleteLineRight();
|
649
|
+
break;
|
650
|
+
}
|
651
|
+
|
652
|
+
} else if (key.ctrl) {
|
653
|
+
/* Control key pressed */
|
654
|
+
|
655
|
+
switch (key.name) {
|
656
|
+
case 'c':
|
657
|
+
if (EventEmitter.listenerCount(this, 'SIGINT') > 0) {
|
658
|
+
this.emit('SIGINT');
|
659
|
+
} else {
|
660
|
+
// This readline instance is finished
|
661
|
+
this.close();
|
662
|
+
}
|
663
|
+
break;
|
664
|
+
|
665
|
+
case 'h': // delete left
|
666
|
+
this._deleteLeft();
|
667
|
+
break;
|
668
|
+
|
669
|
+
case 'd': // delete right or EOF
|
670
|
+
if (this.cursor === 0 && this.line.length === 0) {
|
671
|
+
// This readline instance is finished
|
672
|
+
this.close();
|
673
|
+
} else if (this.cursor < this.line.length) {
|
674
|
+
this._deleteRight();
|
675
|
+
}
|
676
|
+
break;
|
677
|
+
|
678
|
+
case 'u': // delete the whole line
|
679
|
+
this.cursor = 0;
|
680
|
+
this.line = '';
|
681
|
+
this._refreshLine();
|
682
|
+
break;
|
683
|
+
|
684
|
+
case 'k': // delete from current to end of line
|
685
|
+
this._deleteLineRight();
|
686
|
+
break;
|
687
|
+
|
688
|
+
case 'a': // go to the start of the line
|
689
|
+
this._moveCursor(-Infinity);
|
690
|
+
break;
|
691
|
+
|
692
|
+
case 'e': // go to the end of the line
|
693
|
+
this._moveCursor(+Infinity);
|
694
|
+
break;
|
695
|
+
|
696
|
+
case 'b': // back one character
|
697
|
+
this._moveCursor(-1);
|
698
|
+
break;
|
699
|
+
|
700
|
+
case 'f': // forward one character
|
701
|
+
this._moveCursor(+1);
|
702
|
+
break;
|
703
|
+
|
704
|
+
case 'l': // clear the whole screen
|
705
|
+
exports.cursorTo(this.output, 0, 0);
|
706
|
+
exports.clearScreenDown(this.output);
|
707
|
+
this._refreshLine();
|
708
|
+
break;
|
709
|
+
|
710
|
+
case 'n': // next history item
|
711
|
+
this._historyNext();
|
712
|
+
break;
|
713
|
+
|
714
|
+
case 'p': // previous history item
|
715
|
+
this._historyPrev();
|
716
|
+
break;
|
717
|
+
|
718
|
+
case 'z':
|
719
|
+
if (process.platform == 'win32') break;
|
720
|
+
if (EventEmitter.listenerCount(this, 'SIGTSTP') > 0) {
|
721
|
+
this.emit('SIGTSTP');
|
722
|
+
} else {
|
723
|
+
process.once('SIGCONT', (function(self) {
|
724
|
+
return function() {
|
725
|
+
// Don't raise events if stream has already been abandoned.
|
726
|
+
if (!self.paused) {
|
727
|
+
// Stream must be paused and resumed after SIGCONT to catch
|
728
|
+
// SIGINT, SIGTSTP, and EOF.
|
729
|
+
self.pause();
|
730
|
+
self.emit('SIGCONT');
|
731
|
+
}
|
732
|
+
// explicitly re-enable "raw mode" and move the cursor to
|
733
|
+
// the correct position.
|
734
|
+
// See https://github.com/joyent/node/issues/3295.
|
735
|
+
self._setRawMode(true);
|
736
|
+
self._refreshLine();
|
737
|
+
};
|
738
|
+
})(this));
|
739
|
+
this._setRawMode(false);
|
740
|
+
process.kill(process.pid, 'SIGTSTP');
|
741
|
+
}
|
742
|
+
break;
|
743
|
+
|
744
|
+
case 'w': // delete backwards to a word boundary
|
745
|
+
case 'backspace':
|
746
|
+
this._deleteWordLeft();
|
747
|
+
break;
|
748
|
+
|
749
|
+
case 'delete': // delete forward to a word boundary
|
750
|
+
this._deleteWordRight();
|
751
|
+
break;
|
752
|
+
|
753
|
+
case 'backspace':
|
754
|
+
this._deleteWordLeft();
|
755
|
+
break;
|
756
|
+
|
757
|
+
case 'left':
|
758
|
+
this._wordLeft();
|
759
|
+
break;
|
760
|
+
|
761
|
+
case 'right':
|
762
|
+
this._wordRight();
|
763
|
+
break;
|
764
|
+
}
|
765
|
+
|
766
|
+
} else if (key.meta) {
|
767
|
+
/* Meta key pressed */
|
768
|
+
|
769
|
+
switch (key.name) {
|
770
|
+
case 'b': // backward word
|
771
|
+
this._wordLeft();
|
772
|
+
break;
|
773
|
+
|
774
|
+
case 'f': // forward word
|
775
|
+
this._wordRight();
|
776
|
+
break;
|
777
|
+
|
778
|
+
case 'd': // delete forward word
|
779
|
+
case 'delete':
|
780
|
+
this._deleteWordRight();
|
781
|
+
break;
|
782
|
+
|
783
|
+
case 'backspace': // delete backwards to a word boundary
|
784
|
+
this._deleteWordLeft();
|
785
|
+
break;
|
786
|
+
}
|
787
|
+
|
788
|
+
} else {
|
789
|
+
/* No modifier keys used */
|
790
|
+
|
791
|
+
// \r bookkeeping is only relevant if a \n comes right after.
|
792
|
+
if (this._sawReturn && key.name !== 'enter')
|
793
|
+
this._sawReturn = false;
|
794
|
+
|
795
|
+
switch (key.name) {
|
796
|
+
case 'return': // carriage return, i.e. \r
|
797
|
+
this._sawReturn = true;
|
798
|
+
this._line();
|
799
|
+
break;
|
800
|
+
|
801
|
+
case 'enter':
|
802
|
+
if (this._sawReturn)
|
803
|
+
this._sawReturn = false;
|
804
|
+
else
|
805
|
+
this._line();
|
806
|
+
break;
|
807
|
+
|
808
|
+
case 'backspace':
|
809
|
+
this._deleteLeft();
|
810
|
+
break;
|
811
|
+
|
812
|
+
case 'delete':
|
813
|
+
this._deleteRight();
|
814
|
+
break;
|
815
|
+
|
816
|
+
case 'tab': // tab completion
|
817
|
+
this._tabComplete();
|
818
|
+
break;
|
819
|
+
|
820
|
+
case 'left':
|
821
|
+
this._moveCursor(-1);
|
822
|
+
break;
|
823
|
+
|
824
|
+
case 'right':
|
825
|
+
this._moveCursor(+1);
|
826
|
+
break;
|
827
|
+
|
828
|
+
case 'home':
|
829
|
+
this._moveCursor(-Infinity);
|
830
|
+
break;
|
831
|
+
|
832
|
+
case 'end':
|
833
|
+
this._moveCursor(+Infinity);
|
834
|
+
break;
|
835
|
+
|
836
|
+
case 'up':
|
837
|
+
this._historyPrev();
|
838
|
+
break;
|
839
|
+
|
840
|
+
case 'down':
|
841
|
+
this._historyNext();
|
842
|
+
break;
|
843
|
+
|
844
|
+
default:
|
845
|
+
if (util.isBuffer(s))
|
846
|
+
s = s.toString('utf-8');
|
847
|
+
|
848
|
+
if (s) {
|
849
|
+
var lines = s.split(/\r\n|\n|\r/);
|
850
|
+
for (var i = 0, len = lines.length; i < len; i++) {
|
851
|
+
if (i > 0) {
|
852
|
+
this._line();
|
853
|
+
}
|
854
|
+
this._insertString(lines[i]);
|
855
|
+
}
|
856
|
+
}
|
857
|
+
}
|
858
|
+
}
|
859
|
+
};
|
860
|
+
|
861
|
+
|
862
|
+
exports.Interface = Interface;
|
863
|
+
|
864
|
+
|
865
|
+
|
866
|
+
/**
|
867
|
+
* accepts a readable Stream instance and makes it emit "keypress" events
|
868
|
+
*/
|
869
|
+
|
870
|
+
function emitKeypressEvents(stream) {
|
871
|
+
if (stream._keypressDecoder) return;
|
872
|
+
var StringDecoder = require('string_decoder').StringDecoder; // lazy load
|
873
|
+
stream._keypressDecoder = new StringDecoder('utf8');
|
874
|
+
|
875
|
+
function onData(b) {
|
876
|
+
if (EventEmitter.listenerCount(stream, 'keypress') > 0) {
|
877
|
+
var r = stream._keypressDecoder.write(b);
|
878
|
+
if (r) emitKey(stream, r);
|
879
|
+
} else {
|
880
|
+
// Nobody's watching anyway
|
881
|
+
stream.removeListener('data', onData);
|
882
|
+
stream.on('newListener', onNewListener);
|
883
|
+
}
|
884
|
+
}
|
885
|
+
|
886
|
+
function onNewListener(event) {
|
887
|
+
if (event == 'keypress') {
|
888
|
+
stream.on('data', onData);
|
889
|
+
stream.removeListener('newListener', onNewListener);
|
890
|
+
}
|
891
|
+
}
|
892
|
+
|
893
|
+
if (EventEmitter.listenerCount(stream, 'keypress') > 0) {
|
894
|
+
stream.on('data', onData);
|
895
|
+
} else {
|
896
|
+
stream.on('newListener', onNewListener);
|
897
|
+
}
|
898
|
+
}
|
899
|
+
exports.emitKeypressEvents = emitKeypressEvents;
|
900
|
+
|
901
|
+
/*
|
902
|
+
Some patterns seen in terminal key escape codes, derived from combos seen
|
903
|
+
at http://www.midnight-commander.org/browser/lib/tty/key.c
|
904
|
+
|
905
|
+
ESC letter
|
906
|
+
ESC [ letter
|
907
|
+
ESC [ modifier letter
|
908
|
+
ESC [ 1 ; modifier letter
|
909
|
+
ESC [ num char
|
910
|
+
ESC [ num ; modifier char
|
911
|
+
ESC O letter
|
912
|
+
ESC O modifier letter
|
913
|
+
ESC O 1 ; modifier letter
|
914
|
+
ESC N letter
|
915
|
+
ESC [ [ num ; modifier char
|
916
|
+
ESC [ [ 1 ; modifier letter
|
917
|
+
ESC ESC [ num char
|
918
|
+
ESC ESC O letter
|
919
|
+
|
920
|
+
- char is usually ~ but $ and ^ also happen with rxvt
|
921
|
+
- modifier is 1 +
|
922
|
+
(shift * 1) +
|
923
|
+
(left_alt * 2) +
|
924
|
+
(ctrl * 4) +
|
925
|
+
(right_alt * 8)
|
926
|
+
- two leading ESCs apparently mean the same as one leading ESC
|
927
|
+
*/
|
928
|
+
|
929
|
+
// Regexes used for ansi escape code splitting
|
930
|
+
var metaKeyCodeReAnywhere = /(?:\x1b)([a-zA-Z0-9])/;
|
931
|
+
var metaKeyCodeRe = new RegExp('^' + metaKeyCodeReAnywhere.source + '$');
|
932
|
+
var functionKeyCodeReAnywhere =
|
933
|
+
/(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/;
|
934
|
+
var functionKeyCodeRe = new RegExp('^' + functionKeyCodeReAnywhere.source);
|
935
|
+
|
936
|
+
function emitKey(stream, s) {
|
937
|
+
var ch,
|
938
|
+
key = {
|
939
|
+
name: undefined,
|
940
|
+
ctrl: false,
|
941
|
+
meta: false,
|
942
|
+
shift: false
|
943
|
+
},
|
944
|
+
parts;
|
945
|
+
|
946
|
+
if (util.isBuffer(s)) {
|
947
|
+
if (s[0] > 127 && util.isUndefined(s[1])) {
|
948
|
+
s[0] -= 128;
|
949
|
+
s = '\x1b' + s.toString(stream.encoding || 'utf-8');
|
950
|
+
} else {
|
951
|
+
s = s.toString(stream.encoding || 'utf-8');
|
952
|
+
}
|
953
|
+
}
|
954
|
+
|
955
|
+
key.sequence = s;
|
956
|
+
|
957
|
+
if (s === '\r') {
|
958
|
+
// carriage return
|
959
|
+
key.name = 'return';
|
960
|
+
|
961
|
+
} else if (s === '\n') {
|
962
|
+
// enter, should have been called linefeed
|
963
|
+
key.name = 'enter';
|
964
|
+
|
965
|
+
} else if (s === '\t') {
|
966
|
+
// tab
|
967
|
+
key.name = 'tab';
|
968
|
+
|
969
|
+
} else if (s === '\b' || s === '\x7f' ||
|
970
|
+
s === '\x1b\x7f' || s === '\x1b\b') {
|
971
|
+
// backspace or ctrl+h
|
972
|
+
key.name = 'backspace';
|
973
|
+
key.meta = (s.charAt(0) === '\x1b');
|
974
|
+
|
975
|
+
} else if (s === '\x1b' || s === '\x1b\x1b') {
|
976
|
+
// escape key
|
977
|
+
key.name = 'escape';
|
978
|
+
key.meta = (s.length === 2);
|
979
|
+
|
980
|
+
} else if (s === ' ' || s === '\x1b ') {
|
981
|
+
key.name = 'space';
|
982
|
+
key.meta = (s.length === 2);
|
983
|
+
|
984
|
+
} else if (s.length === 1 && s <= '\x1a') {
|
985
|
+
// ctrl+letter
|
986
|
+
key.name = String.fromCharCode(s.charCodeAt(0) + 'a'.charCodeAt(0) - 1);
|
987
|
+
key.ctrl = true;
|
988
|
+
|
989
|
+
} else if (s.length === 1 && s >= 'a' && s <= 'z') {
|
990
|
+
// lowercase letter
|
991
|
+
key.name = s;
|
992
|
+
|
993
|
+
} else if (s.length === 1 && s >= 'A' && s <= 'Z') {
|
994
|
+
// shift+letter
|
995
|
+
key.name = s.toLowerCase();
|
996
|
+
key.shift = true;
|
997
|
+
|
998
|
+
} else if (parts = metaKeyCodeRe.exec(s)) {
|
999
|
+
// meta+character key
|
1000
|
+
key.name = parts[1].toLowerCase();
|
1001
|
+
key.meta = true;
|
1002
|
+
key.shift = /^[A-Z]$/.test(parts[1]);
|
1003
|
+
|
1004
|
+
} else if (parts = functionKeyCodeRe.exec(s)) {
|
1005
|
+
// ansi escape sequence
|
1006
|
+
|
1007
|
+
// reassemble the key code leaving out leading \x1b's,
|
1008
|
+
// the modifier key bitflag and any meaningless "1;" sequence
|
1009
|
+
var code = (parts[1] || '') + (parts[2] || '') +
|
1010
|
+
(parts[4] || '') + (parts[6] || ''),
|
1011
|
+
modifier = (parts[3] || parts[5] || 1) - 1;
|
1012
|
+
|
1013
|
+
// Parse the key modifier
|
1014
|
+
key.ctrl = !!(modifier & 4);
|
1015
|
+
key.meta = !!(modifier & 10);
|
1016
|
+
key.shift = !!(modifier & 1);
|
1017
|
+
key.code = code;
|
1018
|
+
|
1019
|
+
// Parse the key itself
|
1020
|
+
switch (code) {
|
1021
|
+
/* xterm/gnome ESC O letter */
|
1022
|
+
case 'OP': key.name = 'f1'; break;
|
1023
|
+
case 'OQ': key.name = 'f2'; break;
|
1024
|
+
case 'OR': key.name = 'f3'; break;
|
1025
|
+
case 'OS': key.name = 'f4'; break;
|
1026
|
+
|
1027
|
+
/* xterm/rxvt ESC [ number ~ */
|
1028
|
+
case '[11~': key.name = 'f1'; break;
|
1029
|
+
case '[12~': key.name = 'f2'; break;
|
1030
|
+
case '[13~': key.name = 'f3'; break;
|
1031
|
+
case '[14~': key.name = 'f4'; break;
|
1032
|
+
|
1033
|
+
/* from Cygwin and used in libuv */
|
1034
|
+
case '[[A': key.name = 'f1'; break;
|
1035
|
+
case '[[B': key.name = 'f2'; break;
|
1036
|
+
case '[[C': key.name = 'f3'; break;
|
1037
|
+
case '[[D': key.name = 'f4'; break;
|
1038
|
+
case '[[E': key.name = 'f5'; break;
|
1039
|
+
|
1040
|
+
/* common */
|
1041
|
+
case '[15~': key.name = 'f5'; break;
|
1042
|
+
case '[17~': key.name = 'f6'; break;
|
1043
|
+
case '[18~': key.name = 'f7'; break;
|
1044
|
+
case '[19~': key.name = 'f8'; break;
|
1045
|
+
case '[20~': key.name = 'f9'; break;
|
1046
|
+
case '[21~': key.name = 'f10'; break;
|
1047
|
+
case '[23~': key.name = 'f11'; break;
|
1048
|
+
case '[24~': key.name = 'f12'; break;
|
1049
|
+
|
1050
|
+
/* xterm ESC [ letter */
|
1051
|
+
case '[A': key.name = 'up'; break;
|
1052
|
+
case '[B': key.name = 'down'; break;
|
1053
|
+
case '[C': key.name = 'right'; break;
|
1054
|
+
case '[D': key.name = 'left'; break;
|
1055
|
+
case '[E': key.name = 'clear'; break;
|
1056
|
+
case '[F': key.name = 'end'; break;
|
1057
|
+
case '[H': key.name = 'home'; break;
|
1058
|
+
|
1059
|
+
/* xterm/gnome ESC O letter */
|
1060
|
+
case 'OA': key.name = 'up'; break;
|
1061
|
+
case 'OB': key.name = 'down'; break;
|
1062
|
+
case 'OC': key.name = 'right'; break;
|
1063
|
+
case 'OD': key.name = 'left'; break;
|
1064
|
+
case 'OE': key.name = 'clear'; break;
|
1065
|
+
case 'OF': key.name = 'end'; break;
|
1066
|
+
case 'OH': key.name = 'home'; break;
|
1067
|
+
|
1068
|
+
/* xterm/rxvt ESC [ number ~ */
|
1069
|
+
case '[1~': key.name = 'home'; break;
|
1070
|
+
case '[2~': key.name = 'insert'; break;
|
1071
|
+
case '[3~': key.name = 'delete'; break;
|
1072
|
+
case '[4~': key.name = 'end'; break;
|
1073
|
+
case '[5~': key.name = 'pageup'; break;
|
1074
|
+
case '[6~': key.name = 'pagedown'; break;
|
1075
|
+
|
1076
|
+
/* putty */
|
1077
|
+
case '[[5~': key.name = 'pageup'; break;
|
1078
|
+
case '[[6~': key.name = 'pagedown'; break;
|
1079
|
+
|
1080
|
+
/* rxvt */
|
1081
|
+
case '[7~': key.name = 'home'; break;
|
1082
|
+
case '[8~': key.name = 'end'; break;
|
1083
|
+
|
1084
|
+
/* rxvt keys with modifiers */
|
1085
|
+
case '[a': key.name = 'up'; key.shift = true; break;
|
1086
|
+
case '[b': key.name = 'down'; key.shift = true; break;
|
1087
|
+
case '[c': key.name = 'right'; key.shift = true; break;
|
1088
|
+
case '[d': key.name = 'left'; key.shift = true; break;
|
1089
|
+
case '[e': key.name = 'clear'; key.shift = true; break;
|
1090
|
+
|
1091
|
+
case '[2$': key.name = 'insert'; key.shift = true; break;
|
1092
|
+
case '[3$': key.name = 'delete'; key.shift = true; break;
|
1093
|
+
case '[5$': key.name = 'pageup'; key.shift = true; break;
|
1094
|
+
case '[6$': key.name = 'pagedown'; key.shift = true; break;
|
1095
|
+
case '[7$': key.name = 'home'; key.shift = true; break;
|
1096
|
+
case '[8$': key.name = 'end'; key.shift = true; break;
|
1097
|
+
|
1098
|
+
case 'Oa': key.name = 'up'; key.ctrl = true; break;
|
1099
|
+
case 'Ob': key.name = 'down'; key.ctrl = true; break;
|
1100
|
+
case 'Oc': key.name = 'right'; key.ctrl = true; break;
|
1101
|
+
case 'Od': key.name = 'left'; key.ctrl = true; break;
|
1102
|
+
case 'Oe': key.name = 'clear'; key.ctrl = true; break;
|
1103
|
+
|
1104
|
+
case '[2^': key.name = 'insert'; key.ctrl = true; break;
|
1105
|
+
case '[3^': key.name = 'delete'; key.ctrl = true; break;
|
1106
|
+
case '[5^': key.name = 'pageup'; key.ctrl = true; break;
|
1107
|
+
case '[6^': key.name = 'pagedown'; key.ctrl = true; break;
|
1108
|
+
case '[7^': key.name = 'home'; key.ctrl = true; break;
|
1109
|
+
case '[8^': key.name = 'end'; key.ctrl = true; break;
|
1110
|
+
|
1111
|
+
/* misc. */
|
1112
|
+
case '[Z': key.name = 'tab'; key.shift = true; break;
|
1113
|
+
default: key.name = 'undefined'; break;
|
1114
|
+
|
1115
|
+
}
|
1116
|
+
} else if (s.length > 1 && s[0] !== '\x1b') {
|
1117
|
+
// Got a longer-than-one string of characters.
|
1118
|
+
// Probably a paste, since it wasn't a control sequence.
|
1119
|
+
Array.prototype.forEach.call(s, function(c) {
|
1120
|
+
emitKey(stream, c);
|
1121
|
+
});
|
1122
|
+
return;
|
1123
|
+
}
|
1124
|
+
|
1125
|
+
// Don't emit a key if no name was found
|
1126
|
+
if (util.isUndefined(key.name)) {
|
1127
|
+
key = undefined;
|
1128
|
+
}
|
1129
|
+
|
1130
|
+
if (s.length === 1) {
|
1131
|
+
ch = s;
|
1132
|
+
}
|
1133
|
+
|
1134
|
+
if (key || ch) {
|
1135
|
+
stream.emit('keypress', ch, key);
|
1136
|
+
}
|
1137
|
+
}
|
1138
|
+
|
1139
|
+
|
1140
|
+
/**
|
1141
|
+
* moves the cursor to the x and y coordinate on the given stream
|
1142
|
+
*/
|
1143
|
+
|
1144
|
+
function cursorTo(stream, x, y) {
|
1145
|
+
if (!util.isNumber(x) && !util.isNumber(y))
|
1146
|
+
return;
|
1147
|
+
|
1148
|
+
if (!util.isNumber(x))
|
1149
|
+
throw new Error("Can't set cursor row without also setting it's column");
|
1150
|
+
|
1151
|
+
if (!util.isNumber(y)) {
|
1152
|
+
stream.write('\x1b[' + (x + 1) + 'G');
|
1153
|
+
} else {
|
1154
|
+
stream.write('\x1b[' + (y + 1) + ';' + (x + 1) + 'H');
|
1155
|
+
}
|
1156
|
+
}
|
1157
|
+
exports.cursorTo = cursorTo;
|
1158
|
+
|
1159
|
+
|
1160
|
+
/**
|
1161
|
+
* moves the cursor relative to its current location
|
1162
|
+
*/
|
1163
|
+
|
1164
|
+
function moveCursor(stream, dx, dy) {
|
1165
|
+
if (dx < 0) {
|
1166
|
+
stream.write('\x1b[' + (-dx) + 'D');
|
1167
|
+
} else if (dx > 0) {
|
1168
|
+
stream.write('\x1b[' + dx + 'C');
|
1169
|
+
}
|
1170
|
+
|
1171
|
+
if (dy < 0) {
|
1172
|
+
stream.write('\x1b[' + (-dy) + 'A');
|
1173
|
+
} else if (dy > 0) {
|
1174
|
+
stream.write('\x1b[' + dy + 'B');
|
1175
|
+
}
|
1176
|
+
}
|
1177
|
+
exports.moveCursor = moveCursor;
|
1178
|
+
|
1179
|
+
|
1180
|
+
/**
|
1181
|
+
* clears the current line the cursor is on:
|
1182
|
+
* -1 for left of the cursor
|
1183
|
+
* +1 for right of the cursor
|
1184
|
+
* 0 for the entire line
|
1185
|
+
*/
|
1186
|
+
|
1187
|
+
function clearLine(stream, dir) {
|
1188
|
+
if (dir < 0) {
|
1189
|
+
// to the beginning
|
1190
|
+
stream.write('\x1b[1K');
|
1191
|
+
} else if (dir > 0) {
|
1192
|
+
// to the end
|
1193
|
+
stream.write('\x1b[0K');
|
1194
|
+
} else {
|
1195
|
+
// entire line
|
1196
|
+
stream.write('\x1b[2K');
|
1197
|
+
}
|
1198
|
+
}
|
1199
|
+
exports.clearLine = clearLine;
|
1200
|
+
|
1201
|
+
|
1202
|
+
/**
|
1203
|
+
* clears the screen from the current position of the cursor down
|
1204
|
+
*/
|
1205
|
+
|
1206
|
+
function clearScreenDown(stream) {
|
1207
|
+
stream.write('\x1b[0J');
|
1208
|
+
}
|
1209
|
+
exports.clearScreenDown = clearScreenDown;
|
1210
|
+
|
1211
|
+
|
1212
|
+
/**
|
1213
|
+
* Returns the number of columns required to display the given string.
|
1214
|
+
*/
|
1215
|
+
|
1216
|
+
function getStringWidth(str) {
|
1217
|
+
var width = 0;
|
1218
|
+
str = stripVTControlCharacters(str);
|
1219
|
+
for (var i = 0, len = str.length; i < len; i++) {
|
1220
|
+
var code = codePointAt(str, i);
|
1221
|
+
if (code >= 0x10000) { // surrogates
|
1222
|
+
i++;
|
1223
|
+
}
|
1224
|
+
if (isFullWidthCodePoint(code)) {
|
1225
|
+
width += 2;
|
1226
|
+
} else {
|
1227
|
+
width++;
|
1228
|
+
}
|
1229
|
+
}
|
1230
|
+
return width;
|
1231
|
+
}
|
1232
|
+
exports.getStringWidth = getStringWidth;
|
1233
|
+
|
1234
|
+
|
1235
|
+
/**
|
1236
|
+
* Returns true if the character represented by a given
|
1237
|
+
* Unicode code point is full-width. Otherwise returns false.
|
1238
|
+
*/
|
1239
|
+
|
1240
|
+
function isFullWidthCodePoint(code) {
|
1241
|
+
if (isNaN(code)) {
|
1242
|
+
return false;
|
1243
|
+
}
|
1244
|
+
|
1245
|
+
// Code points are derived from:
|
1246
|
+
// http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt
|
1247
|
+
if (code >= 0x1100 && (
|
1248
|
+
code <= 0x115f || // Hangul Jamo
|
1249
|
+
0x2329 === code || // LEFT-POINTING ANGLE BRACKET
|
1250
|
+
0x232a === code || // RIGHT-POINTING ANGLE BRACKET
|
1251
|
+
// CJK Radicals Supplement .. Enclosed CJK Letters and Months
|
1252
|
+
(0x2e80 <= code && code <= 0x3247 && code !== 0x303f) ||
|
1253
|
+
// Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A
|
1254
|
+
0x3250 <= code && code <= 0x4dbf ||
|
1255
|
+
// CJK Unified Ideographs .. Yi Radicals
|
1256
|
+
0x4e00 <= code && code <= 0xa4c6 ||
|
1257
|
+
// Hangul Jamo Extended-A
|
1258
|
+
0xa960 <= code && code <= 0xa97c ||
|
1259
|
+
// Hangul Syllables
|
1260
|
+
0xac00 <= code && code <= 0xd7a3 ||
|
1261
|
+
// CJK Compatibility Ideographs
|
1262
|
+
0xf900 <= code && code <= 0xfaff ||
|
1263
|
+
// Vertical Forms
|
1264
|
+
0xfe10 <= code && code <= 0xfe19 ||
|
1265
|
+
// CJK Compatibility Forms .. Small Form Variants
|
1266
|
+
0xfe30 <= code && code <= 0xfe6b ||
|
1267
|
+
// Halfwidth and Fullwidth Forms
|
1268
|
+
0xff01 <= code && code <= 0xff60 ||
|
1269
|
+
0xffe0 <= code && code <= 0xffe6 ||
|
1270
|
+
// Kana Supplement
|
1271
|
+
0x1b000 <= code && code <= 0x1b001 ||
|
1272
|
+
// Enclosed Ideographic Supplement
|
1273
|
+
0x1f200 <= code && code <= 0x1f251 ||
|
1274
|
+
// CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane
|
1275
|
+
0x20000 <= code && code <= 0x3fffd)) {
|
1276
|
+
return true;
|
1277
|
+
}
|
1278
|
+
return false;
|
1279
|
+
}
|
1280
|
+
exports.isFullWidthCodePoint = isFullWidthCodePoint;
|
1281
|
+
|
1282
|
+
|
1283
|
+
/**
|
1284
|
+
* Returns the Unicode code point for the character at the
|
1285
|
+
* given index in the given string. Similar to String.charCodeAt(),
|
1286
|
+
* but this function handles surrogates (code point >= 0x10000).
|
1287
|
+
*/
|
1288
|
+
|
1289
|
+
function codePointAt(str, index) {
|
1290
|
+
var code = str.charCodeAt(index);
|
1291
|
+
var low;
|
1292
|
+
if (0xd800 <= code && code <= 0xdbff) { // High surrogate
|
1293
|
+
low = str.charCodeAt(index + 1);
|
1294
|
+
if (!isNaN(low)) {
|
1295
|
+
code = 0x10000 + (code - 0xd800) * 0x400 + (low - 0xdc00);
|
1296
|
+
}
|
1297
|
+
}
|
1298
|
+
return code;
|
1299
|
+
}
|
1300
|
+
exports.codePointAt = codePointAt;
|
1301
|
+
|
1302
|
+
|
1303
|
+
/**
|
1304
|
+
* Tries to remove all VT control characters. Use to estimate displayed
|
1305
|
+
* string width. May be buggy due to not running a real state machine
|
1306
|
+
*/
|
1307
|
+
function stripVTControlCharacters(str) {
|
1308
|
+
str = str.replace(new RegExp(functionKeyCodeReAnywhere.source, 'g'), '');
|
1309
|
+
return str.replace(new RegExp(metaKeyCodeReAnywhere.source, 'g'), '');
|
1310
|
+
}
|
1311
|
+
exports.stripVTControlCharacters = stripVTControlCharacters;
|