mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-23 22:02:27 +00:00
Add readline-style completions to Lua/Python/Almquist
This commit is contained in:
parent
933f33bcc1
commit
f6b6204b9e
4 changed files with 121 additions and 103 deletions
97
third_party/linenoise/linenoise.c
vendored
97
third_party/linenoise/linenoise.c
vendored
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
third_party/lua/README.cosmo
vendored
4
third_party/lua/README.cosmo
vendored
|
@ -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.
|
||||||
|
|
122
third_party/lua/lua.main.c
vendored
122
third_party/lua/lua.main.c
vendored
|
@ -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(); */
|
||||||
|
|
1
third_party/lua/lua.mk
vendored
1
third_party/lua/lua.mk
vendored
|
@ -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 \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue