Implement programmable prompt (issue #09)
Replace static _PROMPT/_PROMPT2 with dynamic __prompt(exitcode)/__prompt2(exitcode) functions. Fallback chain: __prompt() → _PROMPT → "> ". Install a default __prompt that shows the current directory with ~ abbreviation. Track last exit code from the REPL loop and pass it to the prompt function.
This commit is contained in:
47
lua.c
47
lua.c
@@ -541,19 +541,38 @@ static void lua_initreadline (lua_State *L) {
|
||||
#endif /* } */
|
||||
|
||||
|
||||
static int last_exit_code = 0; /* tracks exit code for __prompt() */
|
||||
|
||||
|
||||
/*
|
||||
** Return the string to be used as a prompt by the interpreter. Leave
|
||||
** the string (or nil, if using the default value) on the stack, to keep
|
||||
** it anchored.
|
||||
**
|
||||
** Fallback chain: __prompt()/__prompt2() → _PROMPT/_PROMPT2 → "> "/">> "
|
||||
*/
|
||||
static const char *get_prompt (lua_State *L, int firstline) {
|
||||
if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL)
|
||||
return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */
|
||||
else { /* apply 'tostring' over the value */
|
||||
const char *fname = firstline ? "__prompt" : "__prompt2";
|
||||
if (lua_getglobal(L, fname) == LUA_TFUNCTION) {
|
||||
lua_pushinteger(L, last_exit_code);
|
||||
if (lua_pcall(L, 1, 1, 0) == LUA_OK) {
|
||||
const char *p = lua_tostring(L, -1);
|
||||
if (p != NULL) return p; /* string stays on stack, anchored */
|
||||
}
|
||||
lua_pop(L, 1); /* pop error message or non-string result */
|
||||
} else {
|
||||
lua_pop(L, 1); /* pop the non-function value */
|
||||
}
|
||||
/* fallback: check _PROMPT/_PROMPT2 globals (standard Lua compat) */
|
||||
if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") != LUA_TNIL) {
|
||||
const char *p = luaL_tolstring(L, -1, NULL);
|
||||
lua_remove(L, -2); /* remove original value */
|
||||
return p;
|
||||
}
|
||||
lua_pop(L, 1); /* pop nil */
|
||||
/* final fallback: push default string so pushline can pop it */
|
||||
lua_pushstring(L, firstline ? LUA_PROMPT : LUA_PROMPT2);
|
||||
return lua_tostring(L, -1);
|
||||
}
|
||||
|
||||
/* mark in error messages for incomplete statements */
|
||||
@@ -673,6 +692,26 @@ static int loadline (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Install default __prompt function if not already defined (e.g. by config).
|
||||
** Shows current directory with ~ substitution for HOME.
|
||||
*/
|
||||
static void install_default_prompt (lua_State *L) {
|
||||
if (lua_getglobal(L, "__prompt") == LUA_TNIL) {
|
||||
luaL_dostring(L,
|
||||
"function __prompt(exitcode)\n"
|
||||
" local cwd = os.getenv('PWD') or '?'\n"
|
||||
" local home = os.getenv('HOME') or ''\n"
|
||||
" if home ~= '' and cwd:sub(1, #home) == home then\n"
|
||||
" cwd = '~' .. cwd:sub(#home + 1)\n"
|
||||
" end\n"
|
||||
" return cwd .. '> '\n"
|
||||
"end\n");
|
||||
}
|
||||
lua_pop(L, 1); /* pop the getglobal result */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prints (calling the Lua 'print' function) any values on the stack
|
||||
*/
|
||||
@@ -698,6 +737,7 @@ static void doREPL (lua_State *L) {
|
||||
const char *oldprogname = progname;
|
||||
progname = NULL; /* no 'progname' on errors in interactive mode */
|
||||
lua_initreadline(L);
|
||||
install_default_prompt(L);
|
||||
setsignal(SIGINT, repl_sigint_handler);
|
||||
if (sigsetjmp(repl_jmp, 1)) {
|
||||
/* Ctrl-C during input — print newline, clear stack, re-prompt */
|
||||
@@ -708,6 +748,7 @@ static void doREPL (lua_State *L) {
|
||||
while ((status = loadline(L)) != -1) {
|
||||
if (status == LUA_OK)
|
||||
status = docall(L, 0, LUA_MULTRET);
|
||||
last_exit_code = status; /* track for __prompt() */
|
||||
if (status == LUA_OK) l_print(L);
|
||||
else report(L, status);
|
||||
setsignal(SIGINT, repl_sigint_handler); /* re-install after docall */
|
||||
|
||||
Reference in New Issue
Block a user