diff options
-rw-r--r-- | core/.os.luadoc | 3 | ||||
-rw-r--r-- | src/lua.patch | 48 | ||||
-rw-r--r-- | test/test.lua | 63 |
3 files changed, 80 insertions, 34 deletions
diff --git a/core/.os.luadoc b/core/.os.luadoc index 1908c200..6c1b702b 100644 --- a/core/.os.luadoc +++ b/core/.os.luadoc @@ -31,7 +31,7 @@ module('os') -- @param exit_cb Optional Lua function that is called when the child process -- finishes. The child's exit status is passed. -- @return proc or nil plus an error message on failure --- @usage os.spawn('lua buffer.filename', print) +-- @usage os.spawn('lua ' .. buffer.filename, print) -- @usage proc = os.spawn('lua -e "print(io.read())"', print) -- proc:write('foo\n') -- @class function @@ -84,4 +84,3 @@ function spawn_proc:close() end -- @param signal Optional Unix signal to send to *spawn_proc*. The default value -- is 9 (`SIGKILL`), which kills the process. function spawn_proc:kill() end -]] diff --git a/src/lua.patch b/src/lua.patch index c7b6fe91..4bf5fb3f 100644 --- a/src/lua.patch +++ b/src/lua.patch @@ -32,7 +32,7 @@ diff -r 8a23edc91533 src/luaconf.h --- a/src/loslib.c 2017-04-19 13:29:57.000000000 -0400 -+++ b/src/loslib.c 2020-06-22 19:16:23.952024597 -0400 ++++ b/src/loslib.c 2020-07-15 00:31:50.061476181 -0400 @@ -4,6 +4,15 @@ ** See Copyright Notice in lua.h */ @@ -69,7 +69,7 @@ diff -r 8a23edc91533 src/luaconf.h {"time", os_time}, {"tmpname", os_tmpname}, {NULL, NULL} -@@ -404,6 +419,643 @@ +@@ -404,6 +419,633 @@ LUAMOD_API int luaopen_os (lua_State *L) { luaL_newlib(L, syslib); @@ -139,12 +139,19 @@ diff -r 8a23edc91533 src/luaconf.h +static int lp_status(lua_State *L) { + PStream *p = (PStream *)luaL_checkudata(L, 1, "ta_spawn"); + lua_pushstring(L, p->pid ? "running" : "terminated"); - return 1; - } - ++ return 1; ++} ++ +/** Process exit cleanup function. */ +static void exited(PStream *p, int status) { + lua_State *L = p->L; ++ if (p->exit_cb != LUA_REFNIL) { ++ // Call exit callback function with exit status. ++ lua_rawgeti(L, LUA_REGISTRYINDEX, p->exit_cb); ++ lua_pushinteger(L, status); ++ if (lua_pcall(L, 1, 0, 0) != LUA_OK) ++ fprintf(stderr, "Lua: %s\n", lua_tostring(L, -1)), lua_pop(L, 1); ++ } +#if _WIN32 + close(p->pid); +#elif (!GTK || __APPLE__) @@ -296,9 +303,9 @@ diff -r 8a23edc91533 src/luaconf.h + lua_pushfstring(L, "process (pid=%d)", p->pid); + else + lua_pushstring(L, "process (terminated)"); -+ return 1; -+} -+ + return 1; + } + +#if (GTK && !__APPLE__) +/** __gc Lua metamethod. */ +static int lp_gc(lua_State *L) { @@ -352,12 +359,7 @@ diff -r 8a23edc91533 src/luaconf.h + +/** Signal that the child process finished. */ +static void p_exit(GPid pid, int status, void *data) { -+ PStream *p = (PStream *)data; -+ lua_rawgeti(p->L, LUA_REGISTRYINDEX, p->exit_cb); -+ lua_pushinteger(p->L, status); -+ if (lua_pcall(p->L, 1, 0, 0) != LUA_OK) -+ fprintf(stderr, "Lua: %s\n", lua_tostring(p->L, -1)), lua_pop(p->L, 1); -+ exited(p, status); ++ exited((PStream *)data, status); +} +#elif !_WIN32 +/** @@ -419,21 +421,9 @@ diff -r 8a23edc91533 src/luaconf.h + int status; + if (waitpid(p->pid, &status, WNOHANG) > 0) { + fd_read(p->fstdout, p), fd_read(p->fstderr, p); // read anything left -+ if (p->exit_cb != LUA_REFNIL) { -+ lua_rawgeti(L, LUA_REGISTRYINDEX, p->exit_cb); -+ lua_pushinteger(L, status); -+ if (lua_pcall(L, 1, 0, 0) != LUA_OK) -+ fprintf(stderr, "Lua: %s\n", lua_tostring(L, -1)), lua_pop(L, 1); -+ } -+ close(p->fstdin), close(p->fstdout), close(p->fstderr); -+ luaL_unref(L, LUA_REGISTRYINDEX, p->stdout_cb); -+ luaL_unref(L, LUA_REGISTRYINDEX, p->stderr_cb); -+ luaL_unref(L, LUA_REGISTRYINDEX, p->exit_cb); -+ luaL_unref(L, LUA_REGISTRYINDEX, p->ref); // allow proc to be collected -+ p->pid = 0; -+ lua_pushnil(L), lua_replace(L, -2), lua_settable(L, -3); // t[proc] = nil -+ lua_pushnil(L); // push nil for next call to lua_next() -+ } else lua_pop(L, 1); // value ++ exited(p, status); ++ } ++ lua_pop(L, 1); // value + } + lua_pop(L, 1); // spawn_procs + return n; diff --git a/test/test.lua b/test/test.lua index e4e93604..5cf884cb 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1041,6 +1041,59 @@ if CURSES then -- TODO: clipboard, mouse events, etc. end +function test_spawn_cwd() + assert_equal(os.spawn('pwd'):read('a'), lfs.currentdir() .. '\n') + assert_equal(os.spawn('pwd', '/tmp'):read('a'), '/tmp\n') +end + +function test_spawn_env() + assert(not os.spawn('env'):read('a'):find('^%s*$'), 'empty env') + assert(os.spawn('env', {'FOO=bar'}):read('a'):find('FOO=bar\n'), 'env not set') +end + +function test_spawn_stdin() + local p = os.spawn('lua -e "print(io.read())"') + p:write('foo\n') + p:close() + assert_equal(p:read('l'), 'foo') + assert_equal(p:read('a'), '') +end + +function test_spawn_callbacks() + local exit_status = -1 + local p = os.spawn('echo foo', ui.print, nil, function(status) exit_status = status end) + os.execute('sleep 0.1') + ui.update() + assert_equal(buffer._type, _L['[Message Buffer]']) + assert(buffer:get_text():find('^foo'), 'no spawn stdout') + assert_equal(exit_status, 0) + buffer:close(true) + view:unsplit() + -- Verify stdout is not read as stderr. + p = os.spawn('echo foo', nil, ui.print) + os.execute('sleep 0.1') + ui.update() + assert_equal(#_BUFFERS, 1) +end + +function test_spawn_wait() + local exit_status = -1 + local p = os.spawn('sleep 0.1', nil, nil, function(status) exit_status = status end) + assert_equal(p:status(), "running") + assert_equal(p:wait(), 0) + assert_equal(exit_status, 0) + assert_equal(p:status(), "terminated") + -- Verify call to wait again returns previous exit status. + assert_equal(p:wait(), exit_status) +end + +function test_spawn_kill() + local p = os.spawn('sleep 1') + p:kill() + assert(p:wait() ~= 0) + assert_equal(p:status(), "terminated") +end + if WIN32 and CURSES then function test_spawn() -- TODO: @@ -1754,6 +1807,8 @@ function test_editing_convert_indentation() end function test_ui_highlight_word() + local highlight = ui.highlight_words + ui.highlight_words = true buffer.new() buffer:append_text(table.concat({ 'foo', @@ -1796,6 +1851,7 @@ function test_ui_highlight_word() pos = buffer:indicator_end(ui.INDIC_HIGHLIGHT, 2) assert_equal(pos, 1) -- no highlights buffer:close(true) + ui.highlight_words = highlight -- reset end function test_editing_filter_through() @@ -3102,9 +3158,10 @@ function test_set_lexer_style() assert(view.style_fore[style] ~= default_fore, 'function name style_fore same as default style_fore') view.style_fore[style] = view.style_fore[view.STYLE_DEFAULT] assert_equal(view.style_fore[style], default_fore) - assert(lexer.colors.orange > 0 and lexer.colors.orange ~= default_fore) - lexer.styles['function'] = {fore = lexer.colors.orange} - assert_equal(view.style_fore[style], lexer.colors.orange) + local color = lexer.colors[not CURSES and 'orange' or 'blue'] + assert(color > 0 and color ~= default_fore) + lexer.styles['function'] = {fore = color} + assert_equal(view.style_fore[style], color) buffer:close(true) -- Defined in Lua lexer, which is not currently loaded. assert(buffer:style_of_name('library'), view.STYLE_DEFAULT) |