Improve REPL directive support (#348)

* Improve REPL directive support

- use . on column 0 as directive prefix
- use `directives` object properties for genericity
- accept non ambiguous directive abbreviations
- reject invalid directive with extra characters
- simplify `handle_directive` and `handle_cmd`
- document ".help" instead of "\h"
- document ".load"
This commit is contained in:
Charlie Gordon 2024-04-08 15:34:30 +02:00 committed by GitHub
parent d308a13579
commit f62b90daa2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 7081 additions and 7062 deletions

14011
gen/repl.c

File diff suppressed because it is too large Load diff

126
repl.js
View file

@ -217,7 +217,7 @@ import * as os from "os";
var style = style_names[i = j]; var style = style_names[i = j];
while (++j < str.length && style_names[j] == style) while (++j < str.length && style_names[j] == style)
continue; continue;
std.puts(colors[styles[style] || 'default']); std.puts(colors[styles[style] || 'none']);
std.puts(str.substring(i, j)); std.puts(str.substring(i, j));
std.puts(colors['none']); std.puts(colors['none']);
} }
@ -561,11 +561,11 @@ import * as os from "os";
if (pos <= 0) if (pos <= 0)
return g; return g;
var c = line[pos - 1]; var c = line[pos - 1];
if (pos === 1 && c === '\\') if (pos === 1 && (c === '\\' || c === '.'))
return directives; return directives;
if ("'\"`@#)]}\\".indexOf(c) >= 0) if ("'\"`@#)]}\\".indexOf(c) >= 0)
return void 0; return void 0;
if (pos >= 2 && c === ".") { if (c === ".") {
pos--; pos--;
switch (c = line[pos - 1]) { switch (c = line[pos - 1]) {
case '\'': case '\'':
@ -1003,59 +1003,72 @@ import * as os from "os";
print_rec(a); print_rec(a);
} }
function extract_directive(a) { /* return true if the string was a directive */
function handle_directive(a) {
var pos; var pos;
if (a[0] !== '\\') if (a === "?") {
return ""; help();
for (pos = 1; pos < a.length; pos++) { return true;
if (!is_alpha(a[pos])) }
if (a[0] !== '\\' && a[0] !== '.')
return false;
var pos = 1;
while (pos < a.length && a[pos] !== ' ') {
pos++;
}
var cmd = a.substring(1, pos);
var partial = 0;
var fun;
for (var p in directives) {
if (p.startsWith(cmd)) {
fun = directives[p];
partial++;
if (p === cmd) {
partial = 0;
break; break;
} }
return a.substring(1, pos);
} }
}
/* return true if the string after cmd can be evaluted as JS */ if (fun && partial < 2) {
function handle_directive(cmd, expr) { fun(a.substring(pos).trim());
if (cmd === "h" || cmd === "?" || cmd == "help") {
help();
} else if (cmd === "load") {
var filename = expr.substring(cmd.length + 1).trim();
if (filename.lastIndexOf(".") <= filename.lastIndexOf("/"))
filename += ".js";
std.loadScript(filename);
return false;
} else if (cmd === "x") {
hex_mode = true;
} else if (cmd === "d") {
hex_mode = false;
} else if (cmd === "t") {
show_time = !show_time;
} else if (cmd === "clear") {
std.puts("\x1b[H\x1b[J");
} else if (cmd === "q") {
std.exit(0);
} else { } else {
std.puts("Unknown directive: " + cmd + "\n"); std.puts(`Unknown directive: ${cmd}\n`);
return false;
} }
return true; return true;
} }
function help() { function help() {
function sel(n) { var sel = (n) => n ? "*": " ";
return n ? "*": " "; std.puts(".help print this help\n" +
".x " + sel(hex_mode) + "hexadecimal number display\n" +
".dec " + sel(!hex_mode) + "decimal number display\n" +
".time " + sel(show_time) + "toggle timing display\n" +
".color " + sel(show_colors) + "toggle colored output\n" +
".clear clear the terminal\n" +
".load load source code from a file\n" +
".quit exit\n");
} }
std.puts("\\h this help\n" +
"\\x " + sel(hex_mode) + "hexadecimal number display\n" + function load(s) {
"\\d " + sel(!hex_mode) + "decimal number display\n" + if (s.lastIndexOf(".") <= s.lastIndexOf("/"))
"\\t " + sel(show_time) + "toggle timing display\n" + s += ".js";
"\\clear clear the terminal\n" + std.loadScript(s);
"\\q exit\n"); }
function to_bool(s, def) {
return s ? "1 true yes Yes".includes(s) : def;
} }
var directives = Object.setPrototypeOf({ var directives = Object.setPrototypeOf({
h: "h", help: "help", load: "load", x: "x", d: "d", t: "t", "help": help,
clear: "clear", q: "q" }, null); "load": load,
"x": (s) => { hex_mode = to_bool(s, true); },
"dec": (s) => { hex_mode = !to_bool(s, true); },
"time": (s) => { show_time = to_bool(s, !show_time); },
"color": (s) => { show_colors = to_bool(s, !show_colors); },
"clear": () => { std.puts("\x1b[H\x1b[J") },
"quit": () => { std.exit(0); },
}, null);
function eval_and_print(expr) { function eval_and_print(expr) {
var result; var result;
@ -1089,7 +1102,7 @@ import * as os from "os";
} }
function cmd_start() { function cmd_start() {
std.puts('QuickJS-ng - Type "\\h" for help\n'); std.puts('QuickJS-ng - Type ".help" for help\n');
cmd_readline_start(); cmd_readline_start();
} }
@ -1103,28 +1116,15 @@ import * as os from "os";
} }
function handle_cmd(expr) { function handle_cmd(expr) {
var colorstate, cmd; if (!expr)
if (expr === null) {
expr = "";
return; return;
} if (mexpr) {
if (expr === "?") {
help();
return;
}
cmd = extract_directive(expr);
if (cmd.length > 0) {
if (!handle_directive(cmd, expr))
return;
expr = expr.substring(cmd.length + 1);
}
if (expr === "")
return;
if (mexpr)
expr = mexpr + '\n' + expr; expr = mexpr + '\n' + expr;
colorstate = colorize_js(expr); } else {
if (handle_directive(expr))
return;
}
var colorstate = colorize_js(expr);
pstate = colorstate[0]; pstate = colorstate[0];
level = colorstate[1]; level = colorstate[1];
if (pstate) { if (pstate) {