diff options
author | Gregory Anders <greg@gpanders.com> | 2021-08-20 11:45:28 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-20 10:45:28 -0700 |
commit | 50b30de2007961718cc11811a30f6b0f35c3c793 (patch) | |
tree | 5bef15d7394ec3ccef0ced4c7e93d68bb11fa015 | |
parent | 599af74514d0d7083c0565005f255e59ecd7e2ad (diff) | |
download | rneovim-50b30de2007961718cc11811a30f6b0f35c3c793.tar.gz rneovim-50b30de2007961718cc11811a30f6b0f35c3c793.tar.bz2 rneovim-50b30de2007961718cc11811a30f6b0f35c3c793.zip |
feat(terminal): TermClose: set exit code in v:event.status #15406
Closes #4713
-rw-r--r-- | runtime/doc/autocmd.txt | 2 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 1 | ||||
-rw-r--r-- | runtime/doc/nvim_terminal_emulator.txt | 4 | ||||
-rw-r--r-- | src/nvim/buffer.c | 2 | ||||
-rw-r--r-- | src/nvim/channel.c | 4 | ||||
-rw-r--r-- | src/nvim/terminal.c | 13 | ||||
-rw-r--r-- | test/functional/autocmd/termxx_spec.lua | 13 | ||||
-rw-r--r-- | test/functional/fixtures/shell-test.c | 17 |
8 files changed, 43 insertions, 13 deletions
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 87c2e11a1e..03e182cb33 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -914,6 +914,8 @@ TermLeave After leaving |Terminal-mode|. After TermClose. *TermClose* TermClose When a |terminal| job ends. + Sets these |v:event| keys: + status *TermResponse* TermResponse After the response to t_RV is received from the terminal. The value of |v:termresponse| diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index a76298c86c..e02d80252f 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1641,6 +1641,7 @@ v:event Dictionary of event data for the current |autocommand|. Valid |v:false| if not. changed_window Is |v:true| if the the event fired while changing window (or tab) on |DirChanged|. + status Job status or exit code, -1 means "unknown". |TermClose| *v:exception* *exception-variable* v:exception The value of the exception most recently caught and not diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index 6dbc54463c..e0589ba7b8 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -134,6 +134,10 @@ Example: > programs can set this by emitting an escape sequence. - |'channel'| Terminal PTY |job-id|. Can be used with |chansend()| to send input to the terminal. +- The |TermClose| event gives the terminal job exit code in the |v:event| + "status" field. For example, this autocmd closes terminal buffers if the job + exited without error: > + autocmd TermClose * if !v:event.status | exe 'bdelete! '..expand('<abuf>') | endif Use |jobwait()| to check if the terminal job has finished: > let running = jobwait([&channel], 0)[0] == -1 diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 29d4fc786a..a7fc34f2d6 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -532,7 +532,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) } if (buf->terminal) { - terminal_close(buf->terminal, NULL); + terminal_close(buf->terminal, -1); } // Always remove the buffer when there is no file name. diff --git a/src/nvim/channel.c b/src/nvim/channel.c index a0db1bcdfd..0cab916c45 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -698,9 +698,7 @@ static void channel_process_exit_cb(Process *proc, int status, void *data) { Channel *chan = data; if (chan->term) { - char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; - snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status); - terminal_close(chan->term, msg); + terminal_close(chan->term, status); } // If process did not exit, we only closed the handle of a detached process. diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index c07a956dde..45419b8247 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -260,7 +260,7 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts) return rv; } -void terminal_close(Terminal *term, char *msg) +void terminal_close(Terminal *term, int status) { if (term->closed) { return; @@ -278,8 +278,8 @@ void terminal_close(Terminal *term, char *msg) buf_T *buf = handle_get_buffer(term->buf_handle); term->closed = true; - if (!msg || exiting) { - // If no msg was given, this was called by close_buffer(buffer.c). Or if + if (status == -1 || exiting) { + // If status is -1, this was called by close_buffer(buffer.c). Or if // exiting, we must inform the buffer the terminal no longer exists so that // close_buffer() doesn't call this again. term->buf_handle = 0; @@ -291,11 +291,16 @@ void terminal_close(Terminal *term, char *msg) term->opts.close_cb(term->opts.data); } } else { + char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; + snprintf(msg, sizeof msg, "\r\n[Process exited %d]", status); terminal_receive(term, msg, strlen(msg)); } - if (buf) { + if (buf && !is_autocmd_blocked()) { + dict_T *dict = get_vim_var_dict(VV_EVENT); + tv_dict_add_nr(dict, S_LEN("status"), status); apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf); + tv_dict_clear(dict); } } diff --git a/test/functional/autocmd/termxx_spec.lua b/test/functional/autocmd/termxx_spec.lua index b12c24b58d..1e8f981437 100644 --- a/test/functional/autocmd/termxx_spec.lua +++ b/test/functional/autocmd/termxx_spec.lua @@ -13,7 +13,7 @@ describe('autocmd TermClose', function() before_each(function() clear() nvim('set_option', 'shell', nvim_dir .. '/shell-test') - nvim('set_option', 'shellcmdflag', 'EXE') + command('set shellcmdflag=EXE shellredir= shellpipe= shellquote= shellxquote=') end) it('triggers when fast-exiting terminal job stops', function() @@ -90,6 +90,17 @@ describe('autocmd TermClose', function() retry(nil, nil, function() eq('3', eval('g:abuf')) end) feed('<c-c>:qa!<cr>') end) + + it('exposes v:event.status', function() + command('set shellcmdflag=EXIT') + command('autocmd TermClose * let g:status = v:event.status') + + command('terminal 0') + retry(nil, nil, function() eq(0, eval('g:status')) end) + + command('terminal 42') + retry(nil, nil, function() eq(42, eval('g:status')) end) + end) end) it('autocmd TermEnter, TermLeave', function() diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index b95e563932..4196716799 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -19,7 +19,7 @@ static void flush_wait(void) static void help(void) { - puts("A simple implementation of a shell for testing termopen()."); + puts("Fake shell"); puts(""); puts("Usage:"); puts(" shell-test --help"); @@ -42,6 +42,8 @@ static void help(void) puts(" 96: foo bar"); puts(" shell-test INTERACT"); puts(" Prints \"interact $ \" to stderr, and waits for \"exit\" input."); + puts(" shell-test EXIT {code}"); + puts(" Exits immediately with exit code \"{code}\"."); } int main(int argc, char **argv) @@ -103,7 +105,6 @@ int main(int argc, char **argv) char input[256]; char cmd[100]; int arg; - int input_argc; while (1) { fprintf(stderr, "interact $ "); @@ -112,8 +113,7 @@ int main(int argc, char **argv) break; // EOF } - input_argc = sscanf(input, "%99s %d", cmd, &arg); - if(1 == input_argc) { + if(1 == sscanf(input, "%99s %d", cmd, &arg)) { arg = 0; } if (strcmp(cmd, "exit") == 0) { @@ -122,6 +122,15 @@ int main(int argc, char **argv) fprintf(stderr, "command not found: %s\n", cmd); } } + } else if (strcmp(argv[1], "EXIT") == 0) { + int code = 1; + if (argc >= 3) { + if (sscanf(argv[2], "%d", &code) != 1) { + fprintf(stderr, "Invalid exit code: %s\n", argv[2]); + return 2; + } + } + return code; } else { fprintf(stderr, "Unknown first argument: %s\n", argv[1]); return 3; |