diff options
-rw-r--r-- | src/nvim/ex_docmd.c | 44 | ||||
-rw-r--r-- | src/nvim/os/shell.c | 7 | ||||
-rw-r--r-- | test/functional/fixtures/shell-test.c | 16 | ||||
-rw-r--r-- | test/functional/terminal/ex_terminal_spec.lua | 36 |
4 files changed, 83 insertions, 20 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index c7cb875b88..1234f8e888 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -9627,26 +9627,38 @@ static void ex_folddo(exarg_T *eap) static void ex_terminal(exarg_T *eap) { - char *name = (char *)p_sh; // Default to 'shell' if {cmd} is not given. - bool mustfree = false; - char *lquote = "['"; - char *rquote = "']"; + char ex_cmd[1024]; - if (*eap->arg != NUL) { - name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\"); - mustfree = true; - lquote = rquote = "\""; - } + if (*eap->arg != NUL) { // Run {cmd} in 'shell'. + char *name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\"); + snprintf(ex_cmd, sizeof(ex_cmd), + ":enew%s | call termopen(\"%s\") | startinsert", + eap->forceit ? "!" : "", name); + xfree(name); + } else { // No {cmd}: run the job with tokenized 'shell'. + if (*p_sh == NUL) { + EMSG(_(e_shellempty)); + return; + } - char ex_cmd[512]; - snprintf(ex_cmd, sizeof(ex_cmd), - ":enew%s | call termopen(%s%s%s) | startinsert", - eap->forceit==TRUE ? "!" : "", lquote, name, rquote); - do_cmdline_cmd(ex_cmd); + char **argv = shell_build_argv(NULL, NULL); + char **p = argv; + char tempstring[512]; + char shell_argv[512] = { 0 }; - if (mustfree) { - xfree(name); + while (*p != NULL) { + snprintf(tempstring, sizeof(tempstring), ",\"%s\"", *p); + xstrlcat(shell_argv, tempstring, sizeof(shell_argv)); + p++; + } + shell_free_argv(argv); + + snprintf(ex_cmd, sizeof(ex_cmd), + ":enew%s | call termopen([%s]) | startinsert", + eap->forceit ? "!" : "", shell_argv + 1); } + + do_cmdline_cmd(ex_cmd); } /// Checks if `cmd` is "previewable" (i.e. supported by 'inccommand'). diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index a5fd37ed5c..b449cc3d5a 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -39,12 +39,13 @@ typedef struct { #endif /// Builds the argument vector for running the user-configured 'shell' (p_sh) -/// with an optional command prefixed by 'shellcmdflag' (p_shcf). +/// with an optional command prefixed by 'shellcmdflag' (p_shcf). E.g.: +/// +/// ["shell", "-extra_args", "-shellcmdflag", "command with spaces"] /// /// @param cmd Command string, or NULL to run an interactive shell. /// @param extra_args Extra arguments to the shell, or NULL. -/// @return A newly allocated argument vector. It must be freed with -/// `shell_free_argv` when no longer needed. +/// @return Newly allocated argument vector. Must be freed with shell_free_argv. char **shell_build_argv(const char *cmd, const char *extra_args) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC { diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index d9ec254aff..3f3976ece5 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -12,8 +12,12 @@ static void help(void) puts(" shell-test"); puts(" shell-test EXE"); puts(" Prints \"ready $ \" to stderr."); + puts(" shell-test -t {prompt text}"); + puts(" Prints \"{prompt text} $ \" to stderr."); puts(" shell-test EXE \"prog args...\""); puts(" Prints \"ready $ prog args...\\n\" to stderr."); + puts(" shell-test -t {prompt text} EXE \"prog args...\""); + puts(" Prints \"{prompt text} $ progs args...\" to stderr."); puts(" shell-test REP {byte} \"line line line\""); puts(" Prints \"{lnr}: line line line\\n\" to stdout {byte} times."); puts(" I.e. for `shell-test REP ab \"test\"'"); @@ -30,7 +34,17 @@ int main(int argc, char **argv) } if (argc >= 2) { - if (strcmp(argv[1], "EXE") == 0) { + if (strcmp(argv[1], "-t") == 0) { + if (argc < 3) { + fprintf(stderr,"Missing prompt text for -t option\n"); + return 5; + } else { + fprintf(stderr, "%s $ ", argv[2]); + if (argc >= 5 && (strcmp(argv[3], "EXE") == 0)) { + fprintf(stderr, "%s\n", argv[4]); + } + } + } else if (strcmp(argv[1], "EXE") == 0) { fprintf(stderr, "ready $ "); if (argc >= 3) { fprintf(stderr, "%s\n", argv[2]); diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 7a9d2a9b36..9bb683f25f 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -79,6 +79,30 @@ describe(':terminal (with fake shell)', function() ]]) end) + it("with no argument, and 'shell' is set to empty string", function() + nvim('set_option', 'shell', '') + terminal_with_fake_shell() + wait() + screen:expect([[ + ^ | + ~ | + ~ | + E91: 'shell' option is empty | + ]]) + end) + + it("with no argument, but 'shell' has arguments, acts like termopen()", function() + nvim('set_option', 'shell', nvim_dir..'/shell-test -t jeff') + terminal_with_fake_shell() + wait() + screen:expect([[ + jeff $ | + [Process exited 0] | + | + -- TERMINAL -- | + ]]) + end) + it('executes a given command through the shell', function() terminal_with_fake_shell('echo hi') wait() @@ -90,6 +114,18 @@ describe(':terminal (with fake shell)', function() ]]) end) + it("executes a given command through the shell, when 'shell' has arguments", function() + nvim('set_option', 'shell', nvim_dir..'/shell-test -t jeff') + terminal_with_fake_shell('echo hi') + wait() + screen:expect([[ + jeff $ echo hi | + | + [Process exited 0] | + -- TERMINAL -- | + ]]) + end) + it('allows quotes and slashes', function() terminal_with_fake_shell([[echo 'hello' \ "world"]]) wait() |