-
Notifications
You must be signed in to change notification settings - Fork 1
/
javascript.ts
102 lines (95 loc) · 3.42 KB
/
javascript.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import { inspect } from 'util';
import os from 'os';
import repl from 'repl';
import Bluebird from 'bluebird';
import { setupContext } from './context';
import { setupHistory } from './history';
import { getMajorNodeVersion } from './utils';
import { RinoreOptions } from '.';
type ReplServer = repl.REPLServer & { original_eval: repl.REPLEval };
function replaceEval(replServer: repl.REPLServer): ReplServer {
const new_server = Object.assign(replServer, { original_eval: replServer.eval });
const custom_eval: repl.REPLEval = (cmd, context, filename, callback) => {
let assignTo = '';
if (/^\s*([a-zA-Z_$][0-9a-zA-Z_$]*)\s=/.test(cmd)) {
assignTo = RegExp.$1;
}
const runner = new Bluebird((resolve, reject) => {
new_server.original_eval(cmd, context, filename, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
runner.then((result) => {
if (assignTo) {
context[assignTo] = result;
}
callback(null, result);
}).catch((error) => {
callback(error, undefined);
});
};
return Object.assign(new_server, { eval: custom_eval });
}
function replaceCompleter(replServer: any) {
const originalCompleter = replServer.completer;
replServer.completer = (line: string, callback: (error?: any, result?: any) => void) => {
const hasExtraChars = /(?:\(|\s)/.test(line);
line = line.replace(/\(\s*$/, '').trim();
originalCompleter(line, (error?: any, result?: any) => {
if (error || !result[0]) {
// something wrong
callback(error, result);
return;
}
if (!result[0].some((item: any) => item === result[1])) {
// not completed yet
callback(error, result);
return;
}
if (!(result[0].length === 1 || hasExtraChars)) {
// must have only one complete result or extra chars at the end
callback(error, result);
return;
}
const expr = `try { ${result[1]} } catch (e) {}`;
replServer.eval(expr, replServer.context, 'repl', (e?: any, object?: any) => {
if (typeof (object) === 'function') {
const argsMatch = object.toString().match(/^function\s*[^(]*\(\s*([^)]*)\)/m)
|| object.toString().match(/^[^(]*\(\s*([^)]*)\)/m);
replServer.output.write(os.EOL);
replServer.output.write(`${result[1]}(\u001b[35m${argsMatch[1]}\u001b[39m)\r\n`);
replServer._refreshLine();
}
callback(error, [[result[1]], result[1]]);
});
});
};
}
export const start = (rinoreOptions: RinoreOptions): ReplServer => {
const options: { [key: string]: any } = {
historySize: 1000,
input: rinoreOptions.input,
output: rinoreOptions.output,
prompt: rinoreOptions.prompt || 'rinore> ',
terminal: rinoreOptions.terminal,
};
const replServer = repl.start(options);
setupHistory(replServer, rinoreOptions.historyFile || '.rinore_history_js', 1000);
setupContext(replServer);
const new_server = replaceEval(replServer);
if (getMajorNodeVersion() >= 12) {
// show argument on preview
(Function.prototype as any)[inspect.custom] = function () {
const argsMatch = this.toString().match(/^function\s*[^(]*\(\s*([^)]*)\)/m)
|| this.toString().match(/^[^(]*\(\s*([^)]*)\)/m);
return `[Function: ${this.name}(${argsMatch[1]})]`;
};
} else {
replaceCompleter(new_server);
}
return new_server;
};