Add readline-style completions to Lua/Python/Almquist

This commit is contained in:
Justine Tunney 2022-04-16 15:08:00 -07:00
parent 933f33bcc1
commit f6b6204b9e
4 changed files with 121 additions and 103 deletions

View file

@ -793,61 +793,63 @@ static char linenoiseGrow(struct linenoiseState *ls, size_t n) {
return 1; return 1;
} }
/* This is an helper function for linenoiseEdit() and is called when the static size_t linenoiseMaxCompletionLength(linenoiseCompletions *lc) {
* user types the <tab> key in order to complete the string currently in the size_t i, n, m;
* input. for (m = i = 0; i < lc->len; ++i) {
* n = strlen(lc->cvec[i]);
* The state of the editing is encapsulated into the pointed linenoiseState m = MAX(n, m);
* structure as described in the structure definition. */ }
return m;
}
/**
* Performs tab completion similar in behavior to bash and readline.
*/
static ssize_t linenoiseCompleteLine(struct linenoiseState *ls, char *seq, static ssize_t linenoiseCompleteLine(struct linenoiseState *ls, char *seq,
int size) { int size) {
ssize_t nread; ssize_t nread;
size_t i, n, stop; struct abuf ab;
linenoiseCompletions lc; linenoiseCompletions lc;
struct linenoiseState saved; struct linenoiseState saved;
size_t i, j, k, n, perline, itemlen;
// we know that the user pressed tab once
nread = 0; nread = 0;
memset(&lc, 0, sizeof(lc)); bzero(&lc, sizeof(lc));
completionCallback(ls->buf, &lc); completionCallback(ls->buf, &lc);
if (!lc.len) { if (lc.len == 1) {
linenoiseBeep(); // if there's a single completion, complete it, and return
} else { n = strlen(lc.cvec[0]);
i = 0; if (linenoiseGrow(ls, n + 1)) {
stop = 0; memcpy(ls->buf, lc.cvec[0], n + 1);
while (!stop) { ls->len = ls->pos = n;
/* Show completion or original buffer */ }
if (i < lc.len) { linenoiseRefreshLine(ls);
saved = *ls; nread = linenoiseRead(ls->ifd, seq, size, ls);
ls->len = ls->pos = strlen(lc.cvec[i]); } else if (lc.len > 1) {
ls->buf = lc.cvec[i]; // if there's a multiline completions, then do nothing and wait and
linenoiseRefreshLine(ls); // see if the user presses tab again. if the user does this we then
ls->len = saved.len; // print ALL the completions, to above the editing line
ls->pos = saved.pos; nread = linenoiseRead(ls->ifd, seq, size, ls);
ls->buf = saved.buf; if (nread == 1 && seq[0] == '\t') {
} else { itemlen = linenoiseMaxCompletionLength(&lc) + 4;
linenoiseRefreshLine(ls); perline = MAX(1, (ls->ws.ws_col - 1) / itemlen);
} abInit(&ab);
if ((nread = linenoiseRead(ls->ifd, seq, size, ls)) <= 0) { abAppends(&ab, "\r\033[K");
linenoiseFreeCompletions(&lc); for (i = 0; i < lc.len;) {
return -1; for (j = 0; i < lc.len && j < perline; ++j, ++i) {
} n = GetMonospaceWidth(lc.cvec[i], strlen(lc.cvec[i]), 0);
switch (seq[0]) { abAppends(&ab, lc.cvec[i]);
case '\t': for (k = n; k < itemlen; ++k) {
i = (i + 1) % (lc.len + 1); abAppendw(&ab, ' ');
if (i == lc.len) {
linenoiseBeep();
} }
break; }
default: abAppendw(&ab, READ16LE("\r\n"));
if (i < lc.len) {
n = strlen(lc.cvec[i]);
if (linenoiseGrow(ls, n + 1)) {
memcpy(ls->buf, lc.cvec[i], n + 1);
ls->len = ls->pos = n;
}
}
stop = 1;
break;
} }
ab.len -= 2;
abAppends(&ab, "\n");
linenoiseWriteStr(ls->ofd, ab.b);
linenoiseRefreshLine(ls);
abFree(&ab);
} }
} }
linenoiseFreeCompletions(&lc); linenoiseFreeCompletions(&lc);
@ -2074,13 +2076,16 @@ static int linenoiseFallback(const char *prompt, char **res) {
* @return chomped allocated string of read line or null on eof/error * @return chomped allocated string of read line or null on eof/error
*/ */
char *linenoise(const char *prompt) { char *linenoise(const char *prompt) {
bool rm;
char *res; char *res;
if (linenoiseFallback(prompt, &res)) return res; if (linenoiseFallback(prompt, &res)) return res;
fflush(stdout); fflush(stdout);
fflush(stdout); fflush(stdout);
rm = __replmode;
__replmode = true; __replmode = true;
res = linenoiseRaw(prompt, fileno(stdin), fileno(stdout)); res = linenoiseRaw(prompt, fileno(stdin), fileno(stdout));
__replmode = false; __replmode = false;
__replmode = rm;
return res; return res;
} }

View file

@ -20,9 +20,13 @@ PROVENANCE
LOCAL MODIFICATIONS LOCAL MODIFICATIONS
Lua now uses a bestline REPL with bash-style code completion.
Integer literals such as `033` will now be interpreted as octal. Integer literals such as `033` will now be interpreted as octal.
The `\e` string literal escape sequence has been added, which is The `\e` string literal escape sequence has been added, which is
equivalent to `\27` (the Lua version of `\033`) or the ASCII ESC equivalent to `\27` (the Lua version of `\033`) or the ASCII ESC
character. It may be used for teletypewriter control like having character. It may be used for teletypewriter control like having
bold text, which can be encoded elegantly as `\e[1mHELLO\e[0m`. bold text, which can be encoded elegantly as `\e[1mHELLO\e[0m`.
Added luaL_traceback2() for function parameters in traceback.

View file

@ -11,10 +11,11 @@
#include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigaction.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/log/log.h" #include "libc/log/log.h"
#include "libc/runtime/gc.internal.h" #include "libc/runtime/gc.h"
#include "libc/runtime/stack.h" #include "libc/runtime/stack.h"
#include "libc/sysv/consts/exit.h" #include "libc/sysv/consts/exit.h"
#include "libc/x/x.h" #include "libc/x/x.h"
#include "third_party/linenoise/linenoise.h"
#include "third_party/lua/lauxlib.h" #include "third_party/lua/lauxlib.h"
#include "third_party/lua/lprefix.h" #include "third_party/lua/lprefix.h"
#include "third_party/lua/lua.h" #include "third_party/lua/lua.h"
@ -378,68 +379,75 @@ static int handle_luainit (lua_State *L) {
#define LUA_MAXINPUT 512 #define LUA_MAXINPUT 512
#endif #endif
static bool lua_stdin_is_tty(void) {
return isatty(0);
}
/* static bool lua_istartswith(const char *s, const char *prefix) {
** lua_stdin_is_tty detects whether the standard input is a 'tty' (that for (;;) {
** is, whether we're running lua interactively). if (!*prefix) return true;
*/ if (!*s) return false;
#if !defined(lua_stdin_is_tty) /* { */ if (tolower(*s++) != tolower(*prefix++)) return false;
}
}
#if defined(LUA_USE_POSIX) /* { */ static void lua_readline_addcompletion(linenoiseCompletions *c, char *s) {
char **p = c->cvec;
size_t n = c->len + 1;
if ((p = realloc(p, n * sizeof(*p)))) {
p[n - 1] = s;
c->cvec = p;
c->len = n;
}
}
#define lua_stdin_is_tty() isatty(0) static void lua_readline_completions(const char *p, linenoiseCompletions *c) {
lua_State *L;
const char *name;
L = globalL;
lua_pushglobaltable(L);
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
name = lua_tostring(L, -2);
if (lua_istartswith(name, p)) {
lua_readline_addcompletion(c, strdup(name));
}
lua_pop(L, 1);
}
lua_pop(L, 1);
}
#elif defined(LUA_USE_WINDOWS) /* }{ */ static char *lua_readline_hint(const char *p, const char **ansi1, const char **ansi2) {
char *h = 0;
linenoiseCompletions c = {0};
lua_readline_completions(p, &c);
if (c.len == 1) h = strdup(c.cvec[0] + strlen(p));
linenoiseFreeCompletions(&c);
return h;
}
static void lua_initreadline(lua_State *L) {
histpath = xasprintf("%s/.%s_history", _gc(xhomedir()), LUA_PROGNAME);
linenoiseSetCompletionCallback(lua_readline_completions);
linenoiseSetHintsCallback(lua_readline_hint);
linenoiseSetFreeHintsCallback(free);
}
#define lua_stdin_is_tty() _isatty(_fileno(stdin)) static int lua_readline(lua_State *L, char **b, const char *prompt) {
globalL = L;
linenoiseHistoryLoad(histpath);
return !!(*b = linenoise(prompt));
}
#else /* }{ */ static void lua_saveline(lua_State *L, const char *line) {
linenoiseHistoryLoad(histpath);
/* ISO C definition */ linenoiseHistoryAdd(line);
#define lua_stdin_is_tty() 1 /* assume stdin is a tty */ linenoiseHistorySave(histpath);
}
#endif /* } */
#endif /* } */
/*
** lua_readline defines how to show a prompt and then read a line from
** the standard input.
** lua_saveline defines how to "save" a read line in a "history".
** lua_freeline defines how to free a line read by lua_readline.
*/
#if !defined(lua_readline) /* { */
#if defined(LUA_USE_READLINE) /* { */
#define lua_initreadline(L) ((void)L, rl_readline_name="lua")
#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
#define lua_saveline(L,line) ((void)L, add_history(line))
#define lua_freeline(L,b) ((void)L, free(b))
#elif defined(LUA_USE_LINENOISE)
#include "third_party/linenoise/linenoise.h"
#define lua_initreadline(L) (histpath=xasprintf("%s/.%s_history",gc(xhomedir()),LUA_PROGNAME))
#define lua_readline(L,b,p) ((void)L, linenoiseHistoryLoad(histpath), ((b)=linenoise(p)) != NULL)
#define lua_saveline(L,line) ((void)L, linenoiseHistoryLoad(histpath), linenoiseHistoryAdd(line), linenoiseHistorySave(histpath))
#define lua_freeline(L,b) ((void)L, free(b))
#else /* }{ */
#define lua_initreadline(L) ((void)L)
#define lua_readline(L,b,p) \
((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \
fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */
#define lua_saveline(L,line) { (void)L; (void)line; }
#define lua_freeline(L,b) { (void)L; (void)b; }
#endif /* } */
#endif /* } */
static void lua_freeline (lua_State *L, char *b) {
free(b);
}
/* /*
** Return the string to be used as a prompt by the interpreter. Leave ** Return the string to be used as a prompt by the interpreter. Leave
@ -478,7 +486,6 @@ static int incomplete (lua_State *L, int status) {
return 0; /* else... */ return 0; /* else... */
} }
/* /*
** Prompt the user, read a line, and push it into the Lua stack. ** Prompt the user, read a line, and push it into the Lua stack.
*/ */
@ -487,7 +494,7 @@ static int pushline (lua_State *L, int firstline) {
char *b = buffer; char *b = buffer;
size_t l; size_t l;
const char *prmt = get_prompt(L, firstline); const char *prmt = get_prompt(L, firstline);
int readstatus = lua_readline(L, b, prmt); int readstatus = lua_readline(L, &b, prmt);
if (readstatus == 0) if (readstatus == 0)
return 0; /* no input (prompt will be popped by caller) */ return 0; /* no input (prompt will be popped by caller) */
lua_pop(L, 1); /* remove prompt */ lua_pop(L, 1); /* remove prompt */
@ -647,6 +654,7 @@ static int pmain (lua_State *L) {
int main (int argc, char **argv) { int main (int argc, char **argv) {
ShowCrashReports();
int status, result; int status, result;
lua_State *L; lua_State *L;
/* if (IsModeDbg()) ShowCrashReports(); */ /* if (IsModeDbg()) ShowCrashReports(); */

View file

@ -31,6 +31,7 @@ THIRD_PARTY_LUA_DIRECTDEPS = \
LIBC_STDIO \ LIBC_STDIO \
LIBC_STR \ LIBC_STR \
LIBC_SYSV \ LIBC_SYSV \
LIBC_LOG \
LIBC_TIME \ LIBC_TIME \
LIBC_X \ LIBC_X \
LIBC_TINYMATH \ LIBC_TINYMATH \