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

132
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 '\'':
@ -1002,60 +1002,73 @@ import * as os from "os";
} }
print_rec(a); print_rec(a);
} }
function extract_directive(a) {
var pos;
if (a[0] !== '\\')
return "";
for (pos = 1; pos < a.length; pos++) {
if (!is_alpha(a[pos]))
break;
}
return a.substring(1, pos);
}
/* return true if the string after cmd can be evaluted as JS */ /* return true if the string was a directive */
function handle_directive(cmd, expr) { function handle_directive(a) {
if (cmd === "h" || cmd === "?" || cmd == "help") { var pos;
if (a === "?") {
help(); help();
} else if (cmd === "load") { return true;
var filename = expr.substring(cmd.length + 1).trim(); }
if (filename.lastIndexOf(".") <= filename.lastIndexOf("/")) if (a[0] !== '\\' && a[0] !== '.')
filename += ".js";
std.loadScript(filename);
return false; return false;
} else if (cmd === "x") { var pos = 1;
hex_mode = true; while (pos < a.length && a[pos] !== ' ') {
} else if (cmd === "d") { pos++;
hex_mode = false; }
} else if (cmd === "t") { var cmd = a.substring(1, pos);
show_time = !show_time; var partial = 0;
} else if (cmd === "clear") { var fun;
std.puts("\x1b[H\x1b[J"); for (var p in directives) {
} else if (cmd === "q") { if (p.startsWith(cmd)) {
std.exit(0); fun = directives[p];
partial++;
if (p === cmd) {
partial = 0;
break;
}
}
}
if (fun && partial < 2) {
fun(a.substring(pos).trim());
} 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" +
std.puts("\\h this help\n" + ".dec " + sel(!hex_mode) + "decimal number display\n" +
"\\x " + sel(hex_mode) + "hexadecimal number display\n" + ".time " + sel(show_time) + "toggle timing display\n" +
"\\d " + sel(!hex_mode) + "decimal number display\n" + ".color " + sel(show_colors) + "toggle colored output\n" +
"\\t " + sel(show_time) + "toggle timing display\n" + ".clear clear the terminal\n" +
"\\clear clear the terminal\n" + ".load load source code from a file\n" +
"\\q exit\n"); ".quit exit\n");
}
function load(s) {
if (s.lastIndexOf(".") <= s.lastIndexOf("/"))
s += ".js";
std.loadScript(s);
}
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) {