FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Add binding to termios, implement readline for repl · objcode/node@41f213b · GitHub

Commit 41f213b

Browse files
committed
Add binding to termios, implement readline for repl
1 parent a0134ff commit 41f213b

6 files changed

Lines changed: 426 additions & 69 deletions

File tree

‎bin/node-repl‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22

33
puts = require("sys").puts;
44

5-
puts("Welcome to the Node.js REPL.");
6-
puts("Enter ECMAScript at the prompt.");
7-
puts("Tip 1: Use 'rlwrap node-repl' for a better interface");
8-
puts("Tip 2: Type Control-D to exit.");
95
puts("Type '.help' for options.");
106

117
require('repl').start();

‎lib/readline.js‎

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
// Insperation for this code comes from Salvatore Sanfilippo's linenoise.
2+
// http://github.com/antirez/linenoise
3+
// Reference:
4+
// * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
5+
// * http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
6+
7+
var kHistorySize = 30;
8+
var kBufSize = 10*1024;
9+
10+
11+
var Buffer = require('buffer').Buffer;
12+
var inherits = require('sys').inherits;
13+
var EventEmitter = require('events').EventEmitter;
14+
var stdio = process.binding('stdio');
15+
16+
17+
18+
exports.createInterface = function (output, isTTY) {
19+
return new Interface(output, isTTY);
20+
};
21+
22+
function Interface (output, isTTY) {
23+
this.output = output;
24+
25+
this.setPrompt("node> ");
26+
27+
// Current line
28+
this.buf = new Buffer(kBufSize);
29+
this.buf.used = 0;
30+
31+
if (!isTTY) {
32+
this._tty = false;
33+
} else {
34+
// input refers to stdin
35+
36+
// Check process.env.TERM ?
37+
stdio.setRawMode(true);
38+
this._tty = true;
39+
this.columns = stdio.getColumns();
40+
41+
// Cursor position on the line.
42+
this.cursor = 0;
43+
44+
this.history = [];
45+
this.historyIndex = -1;
46+
}
47+
}
48+
49+
inherits(Interface, EventEmitter);
50+
51+
52+
Interface.prototype.setPrompt = function (prompt, length) {
53+
this._prompt = prompt;
54+
this._promptLength = length ? length : Buffer.byteLength(prompt);
55+
};
56+
57+
58+
Interface.prototype.prompt = function () {
59+
if (this._tty) {
60+
this.buf.used = 0;
61+
this.cursor = 0;
62+
this._refreshLine();
63+
} else {
64+
this.output.write(this._prompt);
65+
}
66+
};
67+
68+
69+
Interface.prototype._addHistory = function () {
70+
if (this.buf.used === 0) return "";
71+
72+
var b = new Buffer(this.buf.used);
73+
this.buf.copy(b, 0, 0, this.buf.used);
74+
this.buf.used = 0;
75+
76+
this.history.unshift(b);
77+
this.historyIndex = -1;
78+
79+
this.cursor = 0;
80+
81+
// Only store so many
82+
if (this.history.length > kHistorySize) this.history.pop();
83+
84+
return b.toString('utf8');
85+
};
86+
87+
88+
Interface.prototype._refreshLine = function () {
89+
if (this._closed) return;
90+
91+
stdio.setRawMode(true);
92+
93+
// Cursor to left edge.
94+
this.output.write('\x1b[0G');
95+
96+
// Write the prompt and the current buffer content.
97+
this.output.write(this._prompt);
98+
if (this.buf.used > 0) {
99+
this.output.write(this.buf.slice(0, this.buf.used));
100+
}
101+
102+
// Erase to right.
103+
this.output.write('\x1b[0K');
104+
105+
// Move cursor to original position.
106+
this.output.write('\x1b[0G\x1b[' + (this._promptLength + this.cursor) + 'C');
107+
};
108+
109+
110+
Interface.prototype.close = function (d) {
111+
if (this._tty) {
112+
stdio.setRawMode(false);
113+
}
114+
this.emit('close');
115+
this._closed = true;
116+
};
117+
118+
119+
Interface.prototype.write = function (d) {
120+
if (this._closed) return;
121+
return this._tty ? this._ttyWrite(d) : this._normalWrite(d);
122+
};
123+
124+
125+
Interface.prototype._normalWrite = function (b) {
126+
for (var i = 0; i < b.length; i++) {
127+
var code = b instanceof Buffer ? b[i] : b.charCodeAt(i);
128+
if (code === '\n'.charCodeAt(0) || code === '\r'.charCodeAt(0)) {
129+
var s = this.buf.toString('utf8', 0, this.buf.used);
130+
this.emit('line', s);
131+
this.buf.used = 0;
132+
} else {
133+
this.buf[this.buf.used++] = code;
134+
}
135+
}
136+
};
137+
138+
139+
Interface.prototype._ttyWrite = function (b) {
140+
switch (b[0]) {
141+
/* ctrl+c */
142+
case 3:
143+
//process.kill(process.pid, "SIGINT");
144+
this.close();
145+
break;
146+
147+
case 4: /* ctrl+d */
148+
this.close();
149+
break;
150+
151+
case 13: /* enter */
152+
var line = this._addHistory();
153+
this.output.write('\n\x1b[0G');
154+
stdio.setRawMode(false);
155+
this.emit('line', line);
156+
break;
157+
158+
case 127: /* backspace */
159+
case 8: /* ctrl+h */
160+
if (this.cursor > 0 && this.buf.used > 0) {
161+
for (var i = this.cursor; i < this.buf.used; i++) {
162+
this.buf[i-1] = this.buf[i];
163+
}
164+
this.cursor--;
165+
this.buf.used--;
166+
this._refreshLine();
167+
}
168+
break;
169+
170+
case 21: /* Ctrl+u, delete the whole line. */
171+
this.cursor = this.buf.used = 0;
172+
this._refreshLine();
173+
break;
174+
175+
case 11: /* Ctrl+k, delete from current to end of line. */
176+
this.buf.used = this.cursor;
177+
this._refreshLine();
178+
break;
179+
180+
case 1: /* Ctrl+a, go to the start of the line */
181+
this.cursor = 0;
182+
this._refreshLine();
183+
break;
184+
185+
case 5: /* ctrl+e, go to the end of the line */
186+
this.cursor = this.buf.used;
187+
this._refreshLine();
188+
break;
189+
190+
case 27: /* escape sequence */
191+
if (b[1] === 91 && b[2] === 68) {
192+
// left arrow
193+
if (this.cursor > 0) {
194+
this.cursor--;
195+
this._refreshLine();
196+
}
197+
} else if (b[1] === 91 && b[2] === 67) {
198+
// right arrow
199+
if (this.cursor != this.buf.used) {
200+
this.cursor++;
201+
this._refreshLine();
202+
}
203+
} else if (b[1] === 91 && b[2] === 65) {
204+
// up arrow
205+
if (this.historyIndex + 1 < this.history.length) {
206+
this.historyIndex++;
207+
this.history[this.historyIndex].copy(this.buf, 0);
208+
this.buf.used = this.history[this.historyIndex].length;
209+
// set cursor to end of line.
210+
this.cursor = this.buf.used;
211+
212+
this._refreshLine();
213+
}
214+
215+
} else if (b[1] === 91 && b[2] === 66) {
216+
// down arrow
217+
if (this.historyIndex > 0) {
218+
this.historyIndex--;
219+
this.history[this.historyIndex].copy(this.buf, 0);
220+
this.buf.used = this.history[this.historyIndex].length;
221+
// set cursor to end of line.
222+
this.cursor = this.buf.used;
223+
this._refreshLine();
224+
225+
} else if (this.historyIndex === 0) {
226+
this.historyIndex = -1;
227+
this.cursor = 0;
228+
this.buf.used = 0;
229+
this._refreshLine();
230+
}
231+
}
232+
break;
233+
234+
default:
235+
if (this.buf.used < kBufSize) {
236+
for (var i = this.buf.used + 1; this.cursor < i; i--) {
237+
this.buf[i] = this.buf[i-1];
238+
}
239+
this.buf[this.cursor++] = b[0];
240+
this.buf.used++;
241+
242+
if (this.buf.used == this.cursor) {
243+
this.output.write(b);
244+
} else {
245+
this._refreshLine();
246+
}
247+
}
248+
break;
249+
}
250+
};
251+

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL