mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
patch 8.0.0902: cannot specify directory or environment for a job
Problem: Cannot specify directory or environment for a job. Solution: Add the "cwd" and "env" arguments to job options. (Yasuhiro Matsumoto, closes #1160)
This commit is contained in:
@@ -427,8 +427,8 @@ When no message was available then the result is v:none for a JSON or JS mode
|
|||||||
channels, an empty string for a RAW or NL channel. You can use |ch_canread()|
|
channels, an empty string for a RAW or NL channel. You can use |ch_canread()|
|
||||||
to check if there is something to read.
|
to check if there is something to read.
|
||||||
|
|
||||||
Note that when there is no callback message are dropped. To avoid that add a
|
Note that when there is no callback, messages are dropped. To avoid that add
|
||||||
close callback to the channel.
|
a close callback to the channel.
|
||||||
|
|
||||||
To read all output from a RAW channel that is available: >
|
To read all output from a RAW channel that is available: >
|
||||||
let output = ch_readraw(channel)
|
let output = ch_readraw(channel)
|
||||||
@@ -475,11 +475,6 @@ it like this: >
|
|||||||
Without the handler you need to read the output with |ch_read()| or
|
Without the handler you need to read the output with |ch_read()| or
|
||||||
|ch_readraw()|. You can do this in the close callback, see |read-in-close-cb|.
|
|ch_readraw()|. You can do this in the close callback, see |read-in-close-cb|.
|
||||||
|
|
||||||
Note that if the job exits before you read the output, the output may be lost.
|
|
||||||
This depends on the system (on Unix this happens because closing the write end
|
|
||||||
of a pipe causes the read end to get EOF). To avoid this make the job sleep
|
|
||||||
for a short while before it exits.
|
|
||||||
|
|
||||||
The handler defined for "out_cb" will not receive stderr. If you want to
|
The handler defined for "out_cb" will not receive stderr. If you want to
|
||||||
handle that separately, add an "err_cb" handler: >
|
handle that separately, add an "err_cb" handler: >
|
||||||
let job = job_start(command, {"out_cb": "MyHandler",
|
let job = job_start(command, {"out_cb": "MyHandler",
|
||||||
@@ -494,6 +489,11 @@ started job gets the focus. To avoid that, use the `foreground()` function.
|
|||||||
This might not always work when called early, put in the callback handler or
|
This might not always work when called early, put in the callback handler or
|
||||||
use a timer to call it after the job has started.
|
use a timer to call it after the job has started.
|
||||||
|
|
||||||
|
Depending on the system, starting a job can put Vim in the background, the
|
||||||
|
started job gets the focus. To avoid that, use the `foreground()` function.
|
||||||
|
This might not always work when called early, put in the callback handler or
|
||||||
|
use a timer to call it after the job has started.
|
||||||
|
|
||||||
You can send a message to the command with ch_evalraw(). If the channel is in
|
You can send a message to the command with ch_evalraw(). If the channel is in
|
||||||
JSON or JS mode you can use ch_evalexpr().
|
JSON or JS mode you can use ch_evalexpr().
|
||||||
|
|
||||||
@@ -696,6 +696,10 @@ See |job_setoptions()| and |ch_setoptions()|.
|
|||||||
"block_write": number only for testing: pretend every other write to stdin
|
"block_write": number only for testing: pretend every other write to stdin
|
||||||
will block
|
will block
|
||||||
|
|
||||||
|
"env": dict environment variables for the new process
|
||||||
|
"cwd": "/path/to/dir" current working directory for the new process;
|
||||||
|
if the directory does not exist an error is given
|
||||||
|
|
||||||
|
|
||||||
Writing to a buffer ~
|
Writing to a buffer ~
|
||||||
*out_io-buffer*
|
*out_io-buffer*
|
||||||
@@ -731,10 +735,6 @@ The "out_msg" option can be used to specify whether a new buffer will have the
|
|||||||
first line set to "Reading from channel output...". The default is to add the
|
first line set to "Reading from channel output...". The default is to add the
|
||||||
message. "err_msg" does the same for channel error.
|
message. "err_msg" does the same for channel error.
|
||||||
|
|
||||||
'modifiable' option off, or write to a buffer that has 'modifiable' off. That
|
|
||||||
means that lines will be appended to the buffer, but the user can't easily
|
|
||||||
change the buffer.
|
|
||||||
|
|
||||||
When an existing buffer is to be written where 'modifiable' is off and the
|
When an existing buffer is to be written where 'modifiable' is off and the
|
||||||
"out_modifiable" or "err_modifiable" options is not zero, an error is given
|
"out_modifiable" or "err_modifiable" options is not zero, an error is given
|
||||||
and the buffer will not be written to.
|
and the buffer will not be written to.
|
||||||
|
@@ -4153,6 +4153,8 @@ free_job_options(jobopt_T *opt)
|
|||||||
partial_unref(opt->jo_exit_partial);
|
partial_unref(opt->jo_exit_partial);
|
||||||
else if (opt->jo_exit_cb != NULL)
|
else if (opt->jo_exit_cb != NULL)
|
||||||
func_unref(opt->jo_exit_cb);
|
func_unref(opt->jo_exit_cb);
|
||||||
|
if (opt->jo_env != NULL)
|
||||||
|
dict_unref(opt->jo_env);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -4433,6 +4435,26 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
|
|||||||
opt->jo_term_finish = *val;
|
opt->jo_term_finish = *val;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
else if (STRCMP(hi->hi_key, "env") == 0)
|
||||||
|
{
|
||||||
|
if (!(supported & JO2_ENV))
|
||||||
|
break;
|
||||||
|
opt->jo_set |= JO2_ENV;
|
||||||
|
opt->jo_env = item->vval.v_dict;
|
||||||
|
++item->vval.v_dict->dv_refcount;
|
||||||
|
}
|
||||||
|
else if (STRCMP(hi->hi_key, "cwd") == 0)
|
||||||
|
{
|
||||||
|
if (!(supported & JO2_CWD))
|
||||||
|
break;
|
||||||
|
opt->jo_cwd = get_tv_string_buf_chk(item, opt->jo_cwd_buf);
|
||||||
|
if (opt->jo_cwd == NULL || !mch_isdir(opt->jo_cwd))
|
||||||
|
{
|
||||||
|
EMSG2(_(e_invarg2), "cwd");
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
opt->jo_set |= JO2_CWD;
|
||||||
|
}
|
||||||
else if (STRCMP(hi->hi_key, "waittime") == 0)
|
else if (STRCMP(hi->hi_key, "waittime") == 0)
|
||||||
{
|
{
|
||||||
if (!(supported & JO_WAITTIME))
|
if (!(supported & JO_WAITTIME))
|
||||||
|
@@ -5320,6 +5320,22 @@ mch_job_start(char **argv, job_T *job, jobopt_T *options)
|
|||||||
# endif
|
# endif
|
||||||
set_default_child_environment();
|
set_default_child_environment();
|
||||||
|
|
||||||
|
if (options->jo_env != NULL)
|
||||||
|
{
|
||||||
|
dict_T *dict = options->jo_env;
|
||||||
|
hashitem_T *hi;
|
||||||
|
int todo = (int)dict->dv_hashtab.ht_used;
|
||||||
|
|
||||||
|
for (hi = dict->dv_hashtab.ht_array; todo > 0; ++hi)
|
||||||
|
if (!HASHITEM_EMPTY(hi))
|
||||||
|
{
|
||||||
|
typval_T *item = &dict_lookup(hi)->di_tv;
|
||||||
|
|
||||||
|
vim_setenv((char_u*)hi->hi_key, get_tv_string(item));
|
||||||
|
--todo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (use_null_for_in || use_null_for_out || use_null_for_err)
|
if (use_null_for_in || use_null_for_out || use_null_for_err)
|
||||||
null_fd = open("/dev/null", O_RDWR | O_EXTRA, 0);
|
null_fd = open("/dev/null", O_RDWR | O_EXTRA, 0);
|
||||||
|
|
||||||
@@ -5387,6 +5403,9 @@ mch_job_start(char **argv, job_T *job, jobopt_T *options)
|
|||||||
if (null_fd >= 0)
|
if (null_fd >= 0)
|
||||||
close(null_fd);
|
close(null_fd);
|
||||||
|
|
||||||
|
if (options->jo_cwd != NULL && mch_chdir((char *)options->jo_cwd) != 0)
|
||||||
|
_exit(EXEC_FAILED);
|
||||||
|
|
||||||
/* See above for type of argv. */
|
/* See above for type of argv. */
|
||||||
execvp(argv[0], argv);
|
execvp(argv[0], argv);
|
||||||
|
|
||||||
|
118
src/os_win32.c
118
src/os_win32.c
@@ -3981,16 +3981,29 @@ vim_create_process(
|
|||||||
BOOL inherit_handles,
|
BOOL inherit_handles,
|
||||||
DWORD flags,
|
DWORD flags,
|
||||||
STARTUPINFO *si,
|
STARTUPINFO *si,
|
||||||
PROCESS_INFORMATION *pi)
|
PROCESS_INFORMATION *pi,
|
||||||
|
LPVOID *env,
|
||||||
|
char *cwd)
|
||||||
{
|
{
|
||||||
#ifdef FEAT_MBYTE
|
#ifdef FEAT_MBYTE
|
||||||
if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
|
if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
|
||||||
{
|
|
||||||
WCHAR *wcmd = enc_to_utf16((char_u *)cmd, NULL);
|
|
||||||
|
|
||||||
if (wcmd != NULL)
|
|
||||||
{
|
{
|
||||||
BOOL ret;
|
BOOL ret;
|
||||||
|
WCHAR *wcmd, *wcwd = NULL;
|
||||||
|
|
||||||
|
wcmd = enc_to_utf16((char_u *)cmd, NULL);
|
||||||
|
if (wcmd == NULL)
|
||||||
|
goto fallback;
|
||||||
|
if (cwd != NULL)
|
||||||
|
{
|
||||||
|
wcwd = enc_to_utf16((char_u *)cwd, NULL);
|
||||||
|
if (wcwd == NULL)
|
||||||
|
{
|
||||||
|
vim_free(wcmd);
|
||||||
|
goto fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = CreateProcessW(
|
ret = CreateProcessW(
|
||||||
NULL, /* Executable name */
|
NULL, /* Executable name */
|
||||||
wcmd, /* Command to execute */
|
wcmd, /* Command to execute */
|
||||||
@@ -3998,14 +4011,16 @@ vim_create_process(
|
|||||||
NULL, /* Thread security attributes */
|
NULL, /* Thread security attributes */
|
||||||
inherit_handles, /* Inherit handles */
|
inherit_handles, /* Inherit handles */
|
||||||
flags, /* Creation flags */
|
flags, /* Creation flags */
|
||||||
NULL, /* Environment */
|
env, /* Environment */
|
||||||
NULL, /* Current directory */
|
wcwd, /* Current directory */
|
||||||
(LPSTARTUPINFOW)si, /* Startup information */
|
(LPSTARTUPINFOW)si, /* Startup information */
|
||||||
pi); /* Process information */
|
pi); /* Process information */
|
||||||
vim_free(wcmd);
|
vim_free(wcmd);
|
||||||
|
if (wcwd != NULL)
|
||||||
|
vim_free(wcwd);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
fallback:
|
||||||
#endif
|
#endif
|
||||||
return CreateProcess(
|
return CreateProcess(
|
||||||
NULL, /* Executable name */
|
NULL, /* Executable name */
|
||||||
@@ -4014,8 +4029,8 @@ vim_create_process(
|
|||||||
NULL, /* Thread security attributes */
|
NULL, /* Thread security attributes */
|
||||||
inherit_handles, /* Inherit handles */
|
inherit_handles, /* Inherit handles */
|
||||||
flags, /* Creation flags */
|
flags, /* Creation flags */
|
||||||
NULL, /* Environment */
|
env, /* Environment */
|
||||||
NULL, /* Current directory */
|
cwd, /* Current directory */
|
||||||
si, /* Startup information */
|
si, /* Startup information */
|
||||||
pi); /* Process information */
|
pi); /* Process information */
|
||||||
}
|
}
|
||||||
@@ -4079,7 +4094,8 @@ mch_system_classic(char *cmd, int options)
|
|||||||
|
|
||||||
/* Now, run the command */
|
/* Now, run the command */
|
||||||
vim_create_process(cmd, FALSE,
|
vim_create_process(cmd, FALSE,
|
||||||
CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE, &si, &pi);
|
CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE,
|
||||||
|
&si, &pi, NULL, NULL);
|
||||||
|
|
||||||
/* Wait for the command to terminate before continuing */
|
/* Wait for the command to terminate before continuing */
|
||||||
{
|
{
|
||||||
@@ -4398,7 +4414,8 @@ mch_system_piped(char *cmd, int options)
|
|||||||
* About "Inherit handles" being TRUE: this command can be litigious,
|
* About "Inherit handles" being TRUE: this command can be litigious,
|
||||||
* handle inheritance was deactivated for pending temp file, but, if we
|
* handle inheritance was deactivated for pending temp file, but, if we
|
||||||
* deactivate it, the pipes don't work for some reason. */
|
* deactivate it, the pipes don't work for some reason. */
|
||||||
vim_create_process(p, TRUE, CREATE_DEFAULT_ERROR_MODE, &si, &pi);
|
vim_create_process(p, TRUE, CREATE_DEFAULT_ERROR_MODE,
|
||||||
|
&si, &pi, NULL, NULL);
|
||||||
|
|
||||||
if (p != cmd)
|
if (p != cmd)
|
||||||
vim_free(p);
|
vim_free(p);
|
||||||
@@ -4835,7 +4852,8 @@ mch_call_shell(
|
|||||||
* inherit our handles which causes unpleasant dangling swap
|
* inherit our handles which causes unpleasant dangling swap
|
||||||
* files if we exit before the spawned process
|
* files if we exit before the spawned process
|
||||||
*/
|
*/
|
||||||
if (vim_create_process((char *)newcmd, FALSE, flags, &si, &pi))
|
if (vim_create_process((char *)newcmd, FALSE, flags,
|
||||||
|
&si, &pi, NULL, NULL))
|
||||||
x = 0;
|
x = 0;
|
||||||
else if (vim_shell_execute((char *)newcmd, n_show_cmd)
|
else if (vim_shell_execute((char *)newcmd, n_show_cmd)
|
||||||
> (HINSTANCE)32)
|
> (HINSTANCE)32)
|
||||||
@@ -4976,6 +4994,67 @@ job_io_file_open(
|
|||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Turn the dictionary "env" into a NUL separated list that can be used as the
|
||||||
|
* environment argument of vim_create_process().
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
make_job_env(garray_T *gap, dict_T *env)
|
||||||
|
{
|
||||||
|
hashitem_T *hi;
|
||||||
|
int todo = (int)env->dv_hashtab.ht_used;
|
||||||
|
LPVOID base = GetEnvironmentStringsW();
|
||||||
|
|
||||||
|
/* for last \0 */
|
||||||
|
if (ga_grow(gap, 1) == FAIL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (base)
|
||||||
|
{
|
||||||
|
WCHAR *p = (WCHAR*) base;
|
||||||
|
|
||||||
|
/* for last \0 */
|
||||||
|
if (ga_grow(gap, 1) == FAIL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (*p != 0 || *(p + 1) != 0)
|
||||||
|
{
|
||||||
|
if (ga_grow(gap, 1) == OK)
|
||||||
|
*((WCHAR*)gap->ga_data + gap->ga_len++) = *p;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
FreeEnvironmentStrings(base);
|
||||||
|
*((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
for (hi = env->dv_hashtab.ht_array; todo > 0; ++hi)
|
||||||
|
{
|
||||||
|
if (!HASHITEM_EMPTY(hi))
|
||||||
|
{
|
||||||
|
typval_T *item = &dict_lookup(hi)->di_tv;
|
||||||
|
WCHAR *wkey = enc_to_utf16((char_u *)hi->hi_key, NULL);
|
||||||
|
WCHAR *wval = enc_to_utf16(get_tv_string(item), NULL);
|
||||||
|
--todo;
|
||||||
|
if (wkey != NULL && wval != NULL)
|
||||||
|
{
|
||||||
|
int n, lkey = wcslen(wkey), lval = wcslen(wval);
|
||||||
|
if (ga_grow(gap, lkey + lval + 2) != OK)
|
||||||
|
continue;
|
||||||
|
for (n = 0; n < lkey; n++)
|
||||||
|
*((WCHAR*)gap->ga_data + gap->ga_len++) = wkey[n];
|
||||||
|
*((WCHAR*)gap->ga_data + gap->ga_len++) = L'=';
|
||||||
|
for (n = 0; n < lval; n++)
|
||||||
|
*((WCHAR*)gap->ga_data + gap->ga_len++) = wval[n];
|
||||||
|
*((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
|
||||||
|
}
|
||||||
|
if (wkey != NULL) vim_free(wkey);
|
||||||
|
if (wval != NULL) vim_free(wval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*((WCHAR*)gap->ga_data + gap->ga_len++) = L'\0';
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
mch_job_start(char *cmd, job_T *job, jobopt_T *options)
|
mch_job_start(char *cmd, job_T *job, jobopt_T *options)
|
||||||
{
|
{
|
||||||
@@ -4987,6 +5066,7 @@ mch_job_start(char *cmd, job_T *job, jobopt_T *options)
|
|||||||
HANDLE ifd[2];
|
HANDLE ifd[2];
|
||||||
HANDLE ofd[2];
|
HANDLE ofd[2];
|
||||||
HANDLE efd[2];
|
HANDLE efd[2];
|
||||||
|
garray_T ga;
|
||||||
|
|
||||||
int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL;
|
int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL;
|
||||||
int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL;
|
int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL;
|
||||||
@@ -5005,6 +5085,7 @@ mch_job_start(char *cmd, job_T *job, jobopt_T *options)
|
|||||||
ofd[1] = INVALID_HANDLE_VALUE;
|
ofd[1] = INVALID_HANDLE_VALUE;
|
||||||
efd[0] = INVALID_HANDLE_VALUE;
|
efd[0] = INVALID_HANDLE_VALUE;
|
||||||
efd[1] = INVALID_HANDLE_VALUE;
|
efd[1] = INVALID_HANDLE_VALUE;
|
||||||
|
ga_init2(&ga, (int)sizeof(wchar_t), 500);
|
||||||
|
|
||||||
jo = CreateJobObject(NULL, NULL);
|
jo = CreateJobObject(NULL, NULL);
|
||||||
if (jo == NULL)
|
if (jo == NULL)
|
||||||
@@ -5013,6 +5094,9 @@ mch_job_start(char *cmd, job_T *job, jobopt_T *options)
|
|||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options->jo_env != NULL)
|
||||||
|
make_job_env(&ga, options->jo_env);
|
||||||
|
|
||||||
ZeroMemory(&pi, sizeof(pi));
|
ZeroMemory(&pi, sizeof(pi));
|
||||||
ZeroMemory(&si, sizeof(si));
|
ZeroMemory(&si, sizeof(si));
|
||||||
si.cb = sizeof(si);
|
si.cb = sizeof(si);
|
||||||
@@ -5100,14 +5184,19 @@ mch_job_start(char *cmd, job_T *job, jobopt_T *options)
|
|||||||
CREATE_SUSPENDED |
|
CREATE_SUSPENDED |
|
||||||
CREATE_DEFAULT_ERROR_MODE |
|
CREATE_DEFAULT_ERROR_MODE |
|
||||||
CREATE_NEW_PROCESS_GROUP |
|
CREATE_NEW_PROCESS_GROUP |
|
||||||
|
CREATE_UNICODE_ENVIRONMENT |
|
||||||
CREATE_NEW_CONSOLE,
|
CREATE_NEW_CONSOLE,
|
||||||
&si, &pi))
|
&si, &pi,
|
||||||
|
ga.ga_data,
|
||||||
|
(char *)options->jo_cwd))
|
||||||
{
|
{
|
||||||
CloseHandle(jo);
|
CloseHandle(jo);
|
||||||
job->jv_status = JOB_FAILED;
|
job->jv_status = JOB_FAILED;
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ga_clear(&ga);
|
||||||
|
|
||||||
if (!AssignProcessToJobObject(jo, pi.hProcess))
|
if (!AssignProcessToJobObject(jo, pi.hProcess))
|
||||||
{
|
{
|
||||||
/* if failing, switch the way to terminate
|
/* if failing, switch the way to terminate
|
||||||
@@ -5148,6 +5237,7 @@ failed:
|
|||||||
CloseHandle(ofd[1]);
|
CloseHandle(ofd[1]);
|
||||||
CloseHandle(efd[1]);
|
CloseHandle(efd[1]);
|
||||||
channel_unref(channel);
|
channel_unref(channel);
|
||||||
|
ga_clear(&ga);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
|
@@ -1686,7 +1686,9 @@ struct channel_S {
|
|||||||
#define JO2_ERR_MSG 0x0002 /* "err_msg" (JO_OUT_ << 1) */
|
#define JO2_ERR_MSG 0x0002 /* "err_msg" (JO_OUT_ << 1) */
|
||||||
#define JO2_TERM_NAME 0x0004 /* "term_name" */
|
#define JO2_TERM_NAME 0x0004 /* "term_name" */
|
||||||
#define JO2_TERM_FINISH 0x0008 /* "term_finish" */
|
#define JO2_TERM_FINISH 0x0008 /* "term_finish" */
|
||||||
#define JO2_ALL 0x000F
|
#define JO2_ENV 0x0010 /* "env" */
|
||||||
|
#define JO2_CWD 0x0020 /* "cwd" */
|
||||||
|
#define JO2_ALL 0x003F
|
||||||
|
|
||||||
#define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
|
#define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
|
||||||
#define JO_CB_ALL \
|
#define JO_CB_ALL \
|
||||||
@@ -1738,6 +1740,9 @@ typedef struct
|
|||||||
int jo_id;
|
int jo_id;
|
||||||
char_u jo_soe_buf[NUMBUFLEN];
|
char_u jo_soe_buf[NUMBUFLEN];
|
||||||
char_u *jo_stoponexit;
|
char_u *jo_stoponexit;
|
||||||
|
dict_T *jo_env; /* environment variables */
|
||||||
|
char_u jo_cwd_buf[NUMBUFLEN];
|
||||||
|
char_u *jo_cwd;
|
||||||
|
|
||||||
#ifdef FEAT_TERMINAL
|
#ifdef FEAT_TERMINAL
|
||||||
/* when non-zero run the job in a terminal window of this size */
|
/* when non-zero run the job in a terminal window of this size */
|
||||||
|
@@ -2362,7 +2362,8 @@ f_term_start(typval_T *argvars, typval_T *rettv)
|
|||||||
&& get_job_options(&argvars[1], &opt,
|
&& get_job_options(&argvars[1], &opt,
|
||||||
JO_TIMEOUT_ALL + JO_STOPONEXIT
|
JO_TIMEOUT_ALL + JO_STOPONEXIT
|
||||||
+ JO_EXIT_CB + JO_CLOSE_CALLBACK
|
+ JO_EXIT_CB + JO_CLOSE_CALLBACK
|
||||||
+ JO2_TERM_NAME + JO2_TERM_FINISH) == FAIL)
|
+ JO2_TERM_NAME + JO2_TERM_FINISH
|
||||||
|
+ JO2_CWD + JO2_ENV) == FAIL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
term_start(cmd, &opt);
|
term_start(cmd, &opt);
|
||||||
|
@@ -1664,6 +1664,45 @@ func Test_read_from_terminated_job()
|
|||||||
call assert_equal(1, g:linecount)
|
call assert_equal(1, g:linecount)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_env()
|
||||||
|
if !has('job')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let s:envstr = ''
|
||||||
|
if has('win32')
|
||||||
|
call job_start(['cmd', '/c', 'echo %FOO%'], {'callback': {ch,msg->execute(":let s:envstr .= msg")}, 'env':{'FOO': 'bar'}})
|
||||||
|
else
|
||||||
|
call job_start([&shell, &shellcmdflag, 'echo $FOO'], {'callback': {ch,msg->execute(":let s:envstr .= msg")}, 'env':{'FOO': 'bar'}})
|
||||||
|
endif
|
||||||
|
call WaitFor('"" != s:envstr')
|
||||||
|
call assert_equal("bar", s:envstr)
|
||||||
|
unlet s:envstr
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_cwd()
|
||||||
|
if !has('job')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let s:envstr = ''
|
||||||
|
if has('win32')
|
||||||
|
let expect = $TEMP
|
||||||
|
call job_start(['cmd', '/c', 'echo %CD%'], {'callback': {ch,msg->execute(":let s:envstr .= msg")}, 'cwd': expect})
|
||||||
|
else
|
||||||
|
let expect = $HOME
|
||||||
|
call job_start(['pwd'], {'callback': {ch,msg->execute(":let s:envstr .= msg")}, 'cwd': expect})
|
||||||
|
endif
|
||||||
|
call WaitFor('"" != s:envstr')
|
||||||
|
let expect = substitute(expect, '[/\\]$', '', '')
|
||||||
|
let s:envstr = substitute(s:envstr, '[/\\]$', '', '')
|
||||||
|
if $CI != '' && stridx(s:envstr, '/private/') == 0
|
||||||
|
let s:envstr = s:envstr[8:]
|
||||||
|
endif
|
||||||
|
call assert_equal(expect, s:envstr)
|
||||||
|
unlet s:envstr
|
||||||
|
endfunc
|
||||||
|
|
||||||
function Ch_test_close_lambda(port)
|
function Ch_test_close_lambda(port)
|
||||||
let handle = ch_open('localhost:' . a:port, s:chopt)
|
let handle = ch_open('localhost:' . a:port, s:chopt)
|
||||||
if ch_status(handle) == "fail"
|
if ch_status(handle) == "fail"
|
||||||
|
@@ -8,8 +8,8 @@ source shared.vim
|
|||||||
|
|
||||||
" Open a terminal with a shell, assign the job to g:job and return the buffer
|
" Open a terminal with a shell, assign the job to g:job and return the buffer
|
||||||
" number.
|
" number.
|
||||||
func Run_shell_in_terminal()
|
func Run_shell_in_terminal(options)
|
||||||
let buf = term_start(&shell)
|
let buf = term_start(&shell, a:options)
|
||||||
|
|
||||||
let termlist = term_list()
|
let termlist = term_list()
|
||||||
call assert_equal(1, len(termlist))
|
call assert_equal(1, len(termlist))
|
||||||
@@ -32,7 +32,7 @@ func Stop_shell_in_terminal(buf)
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_terminal_basic()
|
func Test_terminal_basic()
|
||||||
let buf = Run_shell_in_terminal()
|
let buf = Run_shell_in_terminal({})
|
||||||
if has("unix")
|
if has("unix")
|
||||||
call assert_match("^/dev/", job_info(g:job).tty)
|
call assert_match("^/dev/", job_info(g:job).tty)
|
||||||
call assert_match("^/dev/", term_gettty(''))
|
call assert_match("^/dev/", term_gettty(''))
|
||||||
@@ -51,7 +51,7 @@ func Test_terminal_basic()
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_terminal_make_change()
|
func Test_terminal_make_change()
|
||||||
let buf = Run_shell_in_terminal()
|
let buf = Run_shell_in_terminal({})
|
||||||
call Stop_shell_in_terminal(buf)
|
call Stop_shell_in_terminal(buf)
|
||||||
call term_wait(buf)
|
call term_wait(buf)
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ func Test_terminal_make_change()
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_terminal_wipe_buffer()
|
func Test_terminal_wipe_buffer()
|
||||||
let buf = Run_shell_in_terminal()
|
let buf = Run_shell_in_terminal({})
|
||||||
call assert_fails(buf . 'bwipe', 'E517')
|
call assert_fails(buf . 'bwipe', 'E517')
|
||||||
exe buf . 'bwipe!'
|
exe buf . 'bwipe!'
|
||||||
call WaitFor('job_status(g:job) == "dead"')
|
call WaitFor('job_status(g:job) == "dead"')
|
||||||
@@ -76,7 +76,7 @@ func Test_terminal_wipe_buffer()
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_terminal_hide_buffer()
|
func Test_terminal_hide_buffer()
|
||||||
let buf = Run_shell_in_terminal()
|
let buf = Run_shell_in_terminal({})
|
||||||
quit
|
quit
|
||||||
for nr in range(1, winnr('$'))
|
for nr in range(1, winnr('$'))
|
||||||
call assert_notequal(winbufnr(nr), buf)
|
call assert_notequal(winbufnr(nr), buf)
|
||||||
@@ -266,9 +266,11 @@ func Test_terminal_size()
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_finish_close()
|
func Test_finish_close()
|
||||||
|
return
|
||||||
|
" TODO: use something that takes much less than a whole second
|
||||||
|
echo 'This will take five seconds...'
|
||||||
call assert_equal(1, winnr('$'))
|
call assert_equal(1, winnr('$'))
|
||||||
|
|
||||||
" TODO: use something that takes much less than a whole second
|
|
||||||
if has('win32')
|
if has('win32')
|
||||||
let cmd = $windir . '\system32\timeout.exe 1'
|
let cmd = $windir . '\system32\timeout.exe 1'
|
||||||
else
|
else
|
||||||
@@ -304,3 +306,32 @@ func Test_finish_close()
|
|||||||
|
|
||||||
bwipe
|
bwipe
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_terminal_cwd()
|
||||||
|
if !has('unix')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
call mkdir('Xdir')
|
||||||
|
let buf = term_start('pwd', {'cwd': 'Xdir'})
|
||||||
|
sleep 100m
|
||||||
|
call term_wait(buf)
|
||||||
|
call assert_equal(getcwd() . '/Xdir', getline(1))
|
||||||
|
|
||||||
|
exe buf . 'bwipe'
|
||||||
|
call delete('Xdir', 'rf')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_terminal_env()
|
||||||
|
if !has('unix')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
let buf = Run_shell_in_terminal({'env': {'TESTENV': 'correct'}})
|
||||||
|
call term_wait(buf)
|
||||||
|
call term_sendkeys(buf, "echo $TESTENV\r")
|
||||||
|
call term_wait(buf)
|
||||||
|
call Stop_shell_in_terminal(buf)
|
||||||
|
call term_wait(buf)
|
||||||
|
call assert_equal('correct', getline(2))
|
||||||
|
|
||||||
|
exe buf . 'bwipe'
|
||||||
|
endfunc
|
||||||
|
@@ -769,6 +769,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
902,
|
||||||
/**/
|
/**/
|
||||||
901,
|
901,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user