aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Anders <greg@gpanders.com>2021-08-20 11:45:28 -0600
committerGitHub <noreply@github.com>2021-08-20 10:45:28 -0700
commit50b30de2007961718cc11811a30f6b0f35c3c793 (patch)
tree5bef15d7394ec3ccef0ced4c7e93d68bb11fa015
parent599af74514d0d7083c0565005f255e59ecd7e2ad (diff)
downloadrneovim-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.txt2
-rw-r--r--runtime/doc/eval.txt1
-rw-r--r--runtime/doc/nvim_terminal_emulator.txt4
-rw-r--r--src/nvim/buffer.c2
-rw-r--r--src/nvim/channel.c4
-rw-r--r--src/nvim/terminal.c13
-rw-r--r--test/functional/autocmd/termxx_spec.lua13
-rw-r--r--test/functional/fixtures/shell-test.c17
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;