Improve completion in REPL (#343)

* Improve completion in REPL

- refine `get_context_object` to avoid throwing errors (eg: q.<TAB>)
- do not call `eval` in `get_context_object` to avoid throwing errors
  and reduce bloat caused by variable closures.
- support completion of directives
This commit is contained in:
Charlie Gordon 2024-04-06 21:37:19 +02:00 committed by GitHub
parent 02c06d0036
commit fd99929f5d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 11605 additions and 11475 deletions

23017
gen/repl.c

File diff suppressed because it is too large Load diff

63
repl.js
View file

@ -551,51 +551,60 @@ import * as os from "os";
cursor_pos = 0; cursor_pos = 0;
} }
function get_context_word(line, pos) { function get_context_word(line, end) {
var s = ""; var pos = end;
while (pos > 0 && is_word(line[pos - 1])) { while (pos > 0 && is_word(line[pos - 1]))
pos--; pos--;
s = line[pos] + s; return line.slice(pos, end);
}
return s;
} }
function get_context_object(line, pos) { function get_context_object(line, pos) {
var obj, base, c; if (pos <= 0)
if (pos <= 0 || " ~!%^&*(-+={[|:;,<>?/".indexOf(line[pos - 1]) >= 0)
return g; return g;
if (pos >= 2 && line[pos - 1] === ".") { var c = line[pos - 1];
if (pos === 1 && c === '\\')
return directives;
if ("'\"`@#)]}\\".indexOf(c) >= 0)
return void 0;
if (pos >= 2 && c === ".") {
pos--; pos--;
obj = {};
switch (c = line[pos - 1]) { switch (c = line[pos - 1]) {
case '\'': case '\'':
case '\"': case '\"':
case '`':
return "a"; return "a";
case ']': case ']':
return []; return []; // incorrect for a[b].<TAB>
case '}':
return {};
case '/': case '/':
return / /; return / /;
default: default:
if (is_word(c)) { if (is_word(c)) {
base = get_context_word(line, pos); var base = get_context_word(line, pos);
if (["true", "false", "null", "this"].includes(base) || !isNaN(+base)) var base_pos = pos - base.length;
return eval(base); if (base === 'true' || base === 'false')
// Check if `base` is a set of regexp flags return true;
if (pos - base.length >= 3 && line[pos - base.length - 1] === '/') if (base === 'null')
return new RegExp('', base); return null;
obj = get_context_object(line, pos - base.length); if (base === 'this')
return g;
if (!isNaN(+base)) // number literal, incorrect for 1.<TAB>
return 0;
var obj = get_context_object(line, base_pos);
if (obj === null || obj === void 0) if (obj === null || obj === void 0)
return obj; return obj;
if (obj === g && obj[base] === void 0) if (typeof obj[base] !== 'undefined')
return eval(base);
else
return obj[base]; return obj[base];
// Check if `base` is a set of regexp flags
// TODO(chqrlie): this is incorrect for a/i<TAB>...
// Should use colorizer to determine the token type
if (base_pos >= 3 && line[base_pos - 1] === '/' && base.match(/^[dgimsuvy]+$/))
return RegExp();
// base is a local identifier, complete as generic object
} }
return {}; break;
} }
return {};
} }
return void 0; return g;
} }
function get_completions(line, pos) { function get_completions(line, pos) {
@ -1044,6 +1053,10 @@ import * as os from "os";
"\\q exit\n"); "\\q exit\n");
} }
var directives = Object.setPrototypeOf({
h: "h", help: "help", load: "load", x: "x", d: "d", t: "t",
clear: "clear", q: "q" }, null);
function eval_and_print(expr) { function eval_and_print(expr) {
var result; var result;