forked from aniani/vim
patch 7.4.1274
Problem: Cannot run a job. Solution: Add job_start(), job_status() and job_stop(). Currently only works for Unix.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
*channel.txt* For Vim version 7.4. Last change: 2016 Feb 05
|
||||
*channel.txt* For Vim version 7.4. Last change: 2016 Feb 06
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -93,7 +93,7 @@ The default is zero, don't wait, which is useful if the server is supposed to
|
||||
be running already. A negative number waits forever.
|
||||
|
||||
"timeout" is the time to wait for a request when blocking, using
|
||||
ch_sendexpr(). Again in millisecons. The default si 2000 (2 seconds).
|
||||
ch_sendexpr(). Again in milliseconds. The default is 2000 (2 seconds).
|
||||
|
||||
When "mode" is "json" the "msg" argument is the body of the received message,
|
||||
converted to Vim types.
|
||||
@@ -104,13 +104,13 @@ possible to receive a message after sending one.
|
||||
|
||||
The handler can be added or changed later: >
|
||||
call ch_setcallback(handle, {callback})
|
||||
When "callback is empty (zero or an empty string) the handler is removed.
|
||||
When "callback" is empty (zero or an empty string) the handler is removed.
|
||||
NOT IMPLEMENTED YET
|
||||
|
||||
The timeout can be changed later: >
|
||||
call ch_settimeout(handle, {msec})
|
||||
NOT IMPLEMENTED YET
|
||||
|
||||
*E906*
|
||||
Once done with the channel, disconnect it like this: >
|
||||
call ch_close(handle)
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
*eval.txt* For Vim version 7.4. Last change: 2016 Feb 05
|
||||
*eval.txt* For Vim version 7.4. Last change: 2016 Feb 07
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -37,7 +37,7 @@ done, the features in this document are not available. See |+eval| and
|
||||
|
||||
1.1 Variable types ~
|
||||
*E712*
|
||||
There are six types of variables:
|
||||
There are eight types of variables:
|
||||
|
||||
Number A 32 or 64 bit signed number. |expr-number| *Number*
|
||||
Examples: -123 0x10 0177
|
||||
@@ -49,9 +49,6 @@ Float A floating point number. |floating-point-format| *Float*
|
||||
String A NUL terminated string of 8-bit unsigned characters (bytes).
|
||||
|expr-string| Examples: "ab\txx\"--" 'x-z''a,c'
|
||||
|
||||
Funcref A reference to a function |Funcref|.
|
||||
Example: function("strlen")
|
||||
|
||||
List An ordered sequence of items |List|.
|
||||
Example: [1, 2, ['a', 'b']]
|
||||
|
||||
@@ -59,6 +56,13 @@ Dictionary An associative, unordered array: Each entry has a key and a
|
||||
value. |Dictionary|
|
||||
Example: {'blue': "#0000ff", 'red': "#ff0000"}
|
||||
|
||||
Funcref A reference to a function |Funcref|.
|
||||
Example: function("strlen")
|
||||
|
||||
Special v:false, v:true, v:none and v:null
|
||||
|
||||
Job Used for job control, see |job_start()|.
|
||||
|
||||
The Number and String types are converted automatically, depending on how they
|
||||
are used.
|
||||
|
||||
@@ -95,15 +99,16 @@ Note that in the command >
|
||||
"foo" is converted to 0, which means FALSE. To test for a non-empty string,
|
||||
use empty(): >
|
||||
:if !empty("foo")
|
||||
< *E745* *E728* *E703* *E729* *E730* *E731*
|
||||
List, Dictionary and Funcref types are not automatically converted.
|
||||
<
|
||||
*E745* *E728* *E703* *E729* *E730* *E731* *E908* *E910*
|
||||
List, Dictionary, Funcref and Job types are not automatically converted.
|
||||
|
||||
*E805* *E806* *E808*
|
||||
When mixing Number and Float the Number is converted to Float. Otherwise
|
||||
there is no automatic conversion of Float. You can use str2float() for String
|
||||
to Float, printf() for Float to String and float2nr() for Float to Number.
|
||||
|
||||
*E891* *E892* *E893* *E894*
|
||||
*E891* *E892* *E893* *E894* *E907* *E911*
|
||||
When expecting a Float a Number can also be used, but nothing else.
|
||||
|
||||
*E706* *sticky-type-checking*
|
||||
@@ -864,7 +869,7 @@ These three can be repeated and mixed. Examples:
|
||||
expr8 *expr8*
|
||||
-----
|
||||
expr8[expr1] item of String or |List| *expr-[]* *E111*
|
||||
|
||||
*E909*
|
||||
If expr8 is a Number or String this results in a String that contains the
|
||||
expr1'th single byte from expr8. expr8 is used as a String, expr1 as a
|
||||
Number. This doesn't recognize multi-byte encodings, see |byteidx()| for
|
||||
@@ -1947,6 +1952,9 @@ invert( {expr}) Number bitwise invert
|
||||
isdirectory( {directory}) Number TRUE if {directory} is a directory
|
||||
islocked( {expr}) Number TRUE if {expr} is locked
|
||||
items( {dict}) List key-value pairs in {dict}
|
||||
job_start({command} [, {options}]) Job start a job
|
||||
job_status({job}) String get the status of a job
|
||||
job_stop({job} [, {how}]) Number stop a job
|
||||
join( {list} [, {sep}]) String join {list} items into one String
|
||||
jsondecode( {string}) any decode JSON
|
||||
jsonencode( {expr}) String encode JSON
|
||||
@@ -2668,6 +2676,7 @@ confirm({msg} [, {choices} [, {default} [, {type}]]])
|
||||
|
||||
ch_close({handle}) *ch_close()*
|
||||
Close channel {handle}. See |channel|.
|
||||
{only available when compiled with the |+channel| feature}
|
||||
|
||||
ch_open({address} [, {argdict}]) *ch_open()*
|
||||
Open a channel to {address}. See |channel|.
|
||||
@@ -2677,7 +2686,7 @@ ch_open({address} [, {argdict}]) *ch_open()*
|
||||
{address} has the form "hostname:port", e.g.,
|
||||
"localhost:8765".
|
||||
|
||||
If {argdict} is given it must be a |Directory|. The optional
|
||||
If {argdict} is given it must be a |Dictionary|. The optional
|
||||
items are:
|
||||
mode "raw" or "json".
|
||||
Default "json".
|
||||
@@ -2686,10 +2695,11 @@ ch_open({address} [, {argdict}]) *ch_open()*
|
||||
Default: none.
|
||||
waittime Specify connect timeout as milliseconds.
|
||||
Negative means forever.
|
||||
Default: 0.
|
||||
Default: 0 (don't wait)
|
||||
timeout Specify response read timeout value as
|
||||
milliseconds.
|
||||
Default: 2000.
|
||||
{only available when compiled with the |+channel| feature}
|
||||
|
||||
ch_sendexpr({handle}, {expr} [, {callback}]) *ch_sendexpr()*
|
||||
Send {expr} over JSON channel {handle}. See |channel-use|.
|
||||
@@ -2704,10 +2714,14 @@ ch_sendexpr({handle}, {expr} [, {callback}]) *ch_sendexpr()*
|
||||
function. It is called when the response is received. See
|
||||
|channel-callback|.
|
||||
|
||||
{only available when compiled with the |+channel| feature}
|
||||
|
||||
ch_sendraw({handle}, {string} [, {callback}]) *ch_sendraw()*
|
||||
Send {string} over raw channel {handle}. See |channel-raw|.
|
||||
Works like |ch_sendexpr()|, but does not decode the response.
|
||||
|
||||
{only available when compiled with the |+channel| feature}
|
||||
|
||||
*copy()*
|
||||
copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't
|
||||
different from using {expr} directly.
|
||||
@@ -2888,9 +2902,12 @@ diff_hlID({lnum}, {col}) *diff_hlID()*
|
||||
|
||||
empty({expr}) *empty()*
|
||||
Return the Number 1 if {expr} is empty, zero otherwise.
|
||||
A |List| or |Dictionary| is empty when it does not have any
|
||||
items. A Number is empty when its value is zero.
|
||||
|v:false|, |v:none| and |v:null| are empty, |v:true| is not.
|
||||
- A |List| or |Dictionary| is empty when it does not have any
|
||||
items.
|
||||
- A Number and Float is empty when its value is zero.
|
||||
- |v:false|, |v:none| and |v:null| are empty, |v:true| is not.
|
||||
- A Job is empty when it failed to start.
|
||||
|
||||
For a long |List| this is much faster than comparing the
|
||||
length with zero.
|
||||
|
||||
@@ -4286,6 +4303,73 @@ items({dict}) *items()*
|
||||
order.
|
||||
|
||||
|
||||
job_start({command} [, {options}]) *job_start()*
|
||||
Start a job and return a Job object. Unlike |system()| and
|
||||
|:!cmd| this does not wait for the job to finish.
|
||||
|
||||
{command} can be a string. This works best on MS-Windows. On
|
||||
Unix it is split up in white-separated parts to be passed to
|
||||
execvp(). Arguments in double quotes can contain white space.
|
||||
|
||||
{command} can be a list, where the first item is the executable
|
||||
and further items are the arguments. All items are converted
|
||||
to String. This works best on Unix.
|
||||
|
||||
The command is executed directly, not through a shell, the
|
||||
'shell' option is not used. To use the shell: >
|
||||
let job = job_start(["/bin/sh", "-c", "echo hello"])
|
||||
< Or: >
|
||||
let job = job_start('/bin/sh -c "echo hello"')
|
||||
< However, the status of the job will now be the status of the
|
||||
shell, and stopping the job means stopping the shell and the
|
||||
command may continue to run.
|
||||
|
||||
On Unix $PATH is used to search for the executable only when
|
||||
the command does not contain a slash.
|
||||
|
||||
The job will use the same terminal as Vim. If it reads from
|
||||
stdin the job and Vim will be fighting over input, that
|
||||
doesn't work. Redirect stdin and stdout to avoid problems: >
|
||||
let job = job_start(['sh', '-c', "myserver </dev/null >/dev/null"])
|
||||
<
|
||||
The returned Job object can be used to get the status with
|
||||
|job_status()| and stop the job with |job_stop()|.
|
||||
|
||||
{options} must be a Dictionary. It can contain these optional
|
||||
items:
|
||||
killonexit When non-zero kill the job when Vim
|
||||
exits. (default: 0, don't kill)
|
||||
|
||||
{only available when compiled with the |+channel| feature}
|
||||
|
||||
job_status({job}) *job_status()*
|
||||
Returns a String with the status of {job}:
|
||||
"run" job is running
|
||||
"fail" job failed to start
|
||||
"dead" job died or was stopped after running
|
||||
|
||||
{only available when compiled with the |+channel| feature}
|
||||
|
||||
job_stop({job} [, {how}]) *job_stop()*
|
||||
Stop the {job}. This can also be used to signal the job.
|
||||
|
||||
When {how} is omitted or is "term" the job will be terminated
|
||||
normally. For Unix SIGTERM is sent.
|
||||
Other values:
|
||||
"hup" Unix: SIGHUP
|
||||
"quit" Unix: SIGQUIT
|
||||
"kill" Unix: SIGKILL (strongest way to stop)
|
||||
number Unix: signal with that number
|
||||
|
||||
The result is a Number: 1 if the operation could be executed,
|
||||
0 if "how" is not supported on the system.
|
||||
Note that even when the operation was executed, whether the
|
||||
job was actually stopped needs to be checked with
|
||||
job_status().
|
||||
The operation will even be done when the job wasn't running.
|
||||
|
||||
{only available when compiled with the |+channel| feature}
|
||||
|
||||
join({list} [, {sep}]) *join()*
|
||||
Join the items in {list} together into one String.
|
||||
When {sep} is specified it is put in between the items. If
|
||||
@@ -6692,6 +6776,7 @@ type({expr}) The result is a Number, depending on the type of {expr}:
|
||||
Float: 5
|
||||
Boolean: 6 (v:false and v:true)
|
||||
None 7 (v:null and v:none)
|
||||
Job 8
|
||||
To avoid the magic numbers it should be used this way: >
|
||||
:if type(myvar) == type(0)
|
||||
:if type(myvar) == type("")
|
||||
|
315
src/eval.c
315
src/eval.c
@@ -451,6 +451,9 @@ static dict_T *dict_copy(dict_T *orig, int deep, int copyID);
|
||||
static long dict_len(dict_T *d);
|
||||
static char_u *dict2string(typval_T *tv, int copyID);
|
||||
static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate);
|
||||
#ifdef FEAT_JOB
|
||||
static void job_free(job_T *job);
|
||||
#endif
|
||||
static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
|
||||
static char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
|
||||
static char_u *string_quote(char_u *str, int function);
|
||||
@@ -619,6 +622,11 @@ static void f_invert(typval_T *argvars, typval_T *rettv);
|
||||
static void f_isdirectory(typval_T *argvars, typval_T *rettv);
|
||||
static void f_islocked(typval_T *argvars, typval_T *rettv);
|
||||
static void f_items(typval_T *argvars, typval_T *rettv);
|
||||
#ifdef FEAT_JOB
|
||||
static void f_job_start(typval_T *argvars, typval_T *rettv);
|
||||
static void f_job_stop(typval_T *argvars, typval_T *rettv);
|
||||
static void f_job_status(typval_T *argvars, typval_T *rettv);
|
||||
#endif
|
||||
static void f_join(typval_T *argvars, typval_T *rettv);
|
||||
static void f_jsondecode(typval_T *argvars, typval_T *rettv);
|
||||
static void f_jsonencode(typval_T *argvars, typval_T *rettv);
|
||||
@@ -3062,10 +3070,11 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
|
||||
{
|
||||
switch (tv1->v_type)
|
||||
{
|
||||
case VAR_UNKNOWN:
|
||||
case VAR_DICT:
|
||||
case VAR_FUNC:
|
||||
case VAR_SPECIAL:
|
||||
case VAR_UNKNOWN:
|
||||
case VAR_JOB:
|
||||
break;
|
||||
|
||||
case VAR_LIST:
|
||||
@@ -3844,6 +3853,7 @@ item_lock(typval_T *tv, int deep, int lock)
|
||||
case VAR_FUNC:
|
||||
case VAR_FLOAT:
|
||||
case VAR_SPECIAL:
|
||||
case VAR_JOB:
|
||||
break;
|
||||
|
||||
case VAR_LIST:
|
||||
@@ -5339,6 +5349,7 @@ eval_index(
|
||||
return FAIL;
|
||||
#endif
|
||||
case VAR_SPECIAL:
|
||||
case VAR_JOB:
|
||||
if (verbose)
|
||||
EMSG(_("E909: Cannot index a special variable"));
|
||||
return FAIL;
|
||||
@@ -5446,10 +5457,11 @@ eval_index(
|
||||
|
||||
switch (rettv->v_type)
|
||||
{
|
||||
case VAR_SPECIAL:
|
||||
case VAR_UNKNOWN:
|
||||
case VAR_FUNC:
|
||||
case VAR_FLOAT:
|
||||
case VAR_UNKNOWN:
|
||||
case VAR_SPECIAL:
|
||||
case VAR_JOB:
|
||||
break; /* not evaluating, skipping over subscript */
|
||||
|
||||
case VAR_NUMBER:
|
||||
@@ -6167,9 +6179,6 @@ tv_equal(
|
||||
|
||||
switch (tv1->v_type)
|
||||
{
|
||||
case VAR_UNKNOWN:
|
||||
break;
|
||||
|
||||
case VAR_LIST:
|
||||
++recursive_cnt;
|
||||
r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE);
|
||||
@@ -6190,11 +6199,6 @@ tv_equal(
|
||||
case VAR_NUMBER:
|
||||
return tv1->vval.v_number == tv2->vval.v_number;
|
||||
|
||||
#ifdef FEAT_FLOAT
|
||||
case VAR_FLOAT:
|
||||
return tv1->vval.v_float == tv2->vval.v_float;
|
||||
#endif
|
||||
|
||||
case VAR_STRING:
|
||||
s1 = get_tv_string_buf(tv1, buf1);
|
||||
s2 = get_tv_string_buf(tv2, buf2);
|
||||
@@ -6202,6 +6206,17 @@ tv_equal(
|
||||
|
||||
case VAR_SPECIAL:
|
||||
return tv1->vval.v_number == tv2->vval.v_number;
|
||||
|
||||
case VAR_FLOAT:
|
||||
#ifdef FEAT_FLOAT
|
||||
return tv1->vval.v_float == tv2->vval.v_float;
|
||||
#endif
|
||||
case VAR_JOB:
|
||||
#ifdef FEAT_JOB
|
||||
return tv1->vval.v_job == tv2->vval.v_job;
|
||||
#endif
|
||||
case VAR_UNKNOWN:
|
||||
break;
|
||||
}
|
||||
|
||||
/* VAR_UNKNOWN can be the result of a invalid expression, let's say it
|
||||
@@ -6924,7 +6939,7 @@ garbage_collect(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* Free lists and dictionaries that are no longer referenced.
|
||||
* Free lists, dictionaries and jobs that are no longer referenced.
|
||||
*/
|
||||
static int
|
||||
free_unref_items(int copyID)
|
||||
@@ -6969,6 +6984,7 @@ free_unref_items(int copyID)
|
||||
}
|
||||
ll = ll_next;
|
||||
}
|
||||
|
||||
return did_free;
|
||||
}
|
||||
|
||||
@@ -7694,6 +7710,40 @@ failret:
|
||||
return OK;
|
||||
}
|
||||
|
||||
#ifdef FEAT_JOB
|
||||
static void
|
||||
job_free(job_T *job)
|
||||
{
|
||||
/* TODO: free any handles */
|
||||
|
||||
vim_free(job);
|
||||
}
|
||||
|
||||
static void
|
||||
job_unref(job_T *job)
|
||||
{
|
||||
if (job != NULL && --job->jv_refcount <= 0)
|
||||
job_free(job);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a job. Sets the refcount to one.
|
||||
*/
|
||||
static job_T *
|
||||
job_alloc(void)
|
||||
{
|
||||
job_T *job;
|
||||
|
||||
job = (job_T *)alloc_clear(sizeof(job_T));
|
||||
if (job != NULL)
|
||||
{
|
||||
job->jv_refcount = 1;
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static char *
|
||||
get_var_special_name(int nr)
|
||||
{
|
||||
@@ -7789,12 +7839,13 @@ echo_string(
|
||||
case VAR_STRING:
|
||||
case VAR_NUMBER:
|
||||
case VAR_UNKNOWN:
|
||||
case VAR_JOB:
|
||||
*tofree = NULL;
|
||||
r = get_tv_string_buf(tv, numbuf);
|
||||
break;
|
||||
|
||||
#ifdef FEAT_FLOAT
|
||||
case VAR_FLOAT:
|
||||
#ifdef FEAT_FLOAT
|
||||
*tofree = NULL;
|
||||
vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float);
|
||||
r = numbuf;
|
||||
@@ -7844,6 +7895,7 @@ tv2string(
|
||||
case VAR_LIST:
|
||||
case VAR_DICT:
|
||||
case VAR_SPECIAL:
|
||||
case VAR_JOB:
|
||||
case VAR_UNKNOWN:
|
||||
break;
|
||||
}
|
||||
@@ -8148,6 +8200,11 @@ static struct fst
|
||||
{"isdirectory", 1, 1, f_isdirectory},
|
||||
{"islocked", 1, 1, f_islocked},
|
||||
{"items", 1, 1, f_items},
|
||||
#ifdef FEAT_CHANNEL
|
||||
{"job_start", 1, 2, f_job_start},
|
||||
{"job_status", 1, 1, f_job_status},
|
||||
{"job_stop", 1, 1, f_job_stop},
|
||||
#endif
|
||||
{"join", 1, 2, f_join},
|
||||
{"jsondecode", 1, 1, f_jsondecode},
|
||||
{"jsonencode", 1, 1, f_jsonencode},
|
||||
@@ -10535,8 +10592,8 @@ f_empty(typval_T *argvars, typval_T *rettv)
|
||||
case VAR_NUMBER:
|
||||
n = argvars[0].vval.v_number == 0;
|
||||
break;
|
||||
#ifdef FEAT_FLOAT
|
||||
case VAR_FLOAT:
|
||||
#ifdef FEAT_FLOAT
|
||||
n = argvars[0].vval.v_float == 0.0;
|
||||
break;
|
||||
#endif
|
||||
@@ -10552,6 +10609,11 @@ f_empty(typval_T *argvars, typval_T *rettv)
|
||||
n = argvars[0].vval.v_number != VVAL_TRUE;
|
||||
break;
|
||||
|
||||
case VAR_JOB:
|
||||
#ifdef FEAT_JOB
|
||||
n = argvars[0].vval.v_job->jv_status != JOB_STARTED;
|
||||
break;
|
||||
#endif
|
||||
case VAR_UNKNOWN:
|
||||
EMSG2(_(e_intern2), "f_empty(UNKNOWN)");
|
||||
n = TRUE;
|
||||
@@ -13060,6 +13122,9 @@ f_has(typval_T *argvars, typval_T *rettv)
|
||||
#ifdef FEAT_INS_EXPAND
|
||||
"insert_expand",
|
||||
#endif
|
||||
#ifdef FEAT_JOB
|
||||
"job",
|
||||
#endif
|
||||
#ifdef FEAT_JUMPLIST
|
||||
"jumplist",
|
||||
#endif
|
||||
@@ -14188,6 +14253,161 @@ f_items(typval_T *argvars, typval_T *rettv)
|
||||
dict_list(argvars, rettv, 2);
|
||||
}
|
||||
|
||||
#ifdef FEAT_JOB
|
||||
/*
|
||||
* "job_start()" function
|
||||
*/
|
||||
static void
|
||||
f_job_start(typval_T *argvars UNUSED, typval_T *rettv)
|
||||
{
|
||||
job_T *job;
|
||||
char_u *cmd = NULL;
|
||||
#if defined(UNIX)
|
||||
# define USE_ARGV
|
||||
char **argv = NULL;
|
||||
int argc = 0;
|
||||
#else
|
||||
garray_T ga;
|
||||
#endif
|
||||
|
||||
rettv->v_type = VAR_JOB;
|
||||
job = job_alloc();
|
||||
rettv->vval.v_job = job;
|
||||
if (job == NULL)
|
||||
return;
|
||||
|
||||
rettv->vval.v_job->jv_status = JOB_FAILED;
|
||||
#ifndef USE_ARGV
|
||||
ga_init2(&ga, 200);
|
||||
#endif
|
||||
|
||||
if (argvars[0].v_type == VAR_STRING)
|
||||
{
|
||||
/* Command is a string. */
|
||||
cmd = argvars[0].vval.v_string;
|
||||
#ifdef USE_ARGV
|
||||
if (mch_parse_cmd(cmd, FALSE, &argv, &argc) == FAIL)
|
||||
return;
|
||||
argv[argc] = NULL;
|
||||
#endif
|
||||
}
|
||||
else if (argvars[0].v_type != VAR_LIST
|
||||
|| argvars[0].vval.v_list == NULL
|
||||
|| argvars[0].vval.v_list->lv_len < 1)
|
||||
{
|
||||
EMSG(_(e_invarg));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
list_T *l = argvars[0].vval.v_list;
|
||||
listitem_T *li;
|
||||
char_u *s;
|
||||
|
||||
#ifdef USE_ARGV
|
||||
/* Pass argv[] to mch_call_shell(). */
|
||||
argv = (char **)alloc(sizeof(char *) * (l->lv_len + 1));
|
||||
if (argv == NULL)
|
||||
return;
|
||||
#endif
|
||||
for (li = l->lv_first; li != NULL; li = li->li_next)
|
||||
{
|
||||
s = get_tv_string_chk(&li->li_tv);
|
||||
if (s == NULL)
|
||||
goto theend;
|
||||
#ifdef USE_ARGV
|
||||
argv[argc++] = (char *)s;
|
||||
#else
|
||||
if (li != l->lv_first)
|
||||
{
|
||||
s = vim_strsave_shellescape(s, FALSE, TRUE);
|
||||
if (s == NULL)
|
||||
goto theend;
|
||||
}
|
||||
ga_concat(&ga, s);
|
||||
vim_free(s);
|
||||
if (li->li_next != NULL)
|
||||
ga_append(&ga, ' ');
|
||||
#endif
|
||||
}
|
||||
#ifdef USE_ARGV
|
||||
argv[argc] = NULL;
|
||||
#else
|
||||
cmd = ga.ga_data;
|
||||
#endif
|
||||
}
|
||||
#ifdef USE_ARGV
|
||||
mch_start_job(argv, job);
|
||||
#else
|
||||
mch_start_job(cmd, job);
|
||||
#endif
|
||||
|
||||
theend:
|
||||
#ifdef USE_ARGV
|
||||
if (argv != NULL)
|
||||
vim_free(argv);
|
||||
#else
|
||||
vim_free(ga.ga_data);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* "job_status()" function
|
||||
*/
|
||||
static void
|
||||
f_job_status(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
|
||||
{
|
||||
char *result;
|
||||
|
||||
if (argvars[0].v_type != VAR_JOB)
|
||||
EMSG(_(e_invarg));
|
||||
else
|
||||
{
|
||||
job_T *job = argvars[0].vval.v_job;
|
||||
|
||||
if (job->jv_status == JOB_ENDED)
|
||||
/* No need to check, dead is dead. */
|
||||
result = "dead";
|
||||
else if (job->jv_status == JOB_FAILED)
|
||||
result = "fail";
|
||||
else
|
||||
result = mch_job_status(job);
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = vim_strsave((char_u *)result);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "job_stop()" function
|
||||
*/
|
||||
static void
|
||||
f_job_stop(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
|
||||
{
|
||||
if (argvars[0].v_type != VAR_JOB)
|
||||
EMSG(_(e_invarg));
|
||||
else
|
||||
{
|
||||
char_u *arg;
|
||||
|
||||
if (argvars[1].v_type == VAR_UNKNOWN)
|
||||
arg = (char_u *)"";
|
||||
else
|
||||
{
|
||||
arg = get_tv_string_chk(&argvars[1]);
|
||||
if (arg == NULL)
|
||||
{
|
||||
EMSG(_(e_invarg));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mch_stop_job(argvars[0].vval.v_job, arg) == FAIL)
|
||||
rettv->vval.v_number = 0;
|
||||
else
|
||||
rettv->vval.v_number = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* "join()" function
|
||||
*/
|
||||
@@ -14295,6 +14515,7 @@ f_len(typval_T *argvars, typval_T *rettv)
|
||||
case VAR_SPECIAL:
|
||||
case VAR_FLOAT:
|
||||
case VAR_FUNC:
|
||||
case VAR_JOB:
|
||||
EMSG(_("E701: Invalid type for len()"));
|
||||
break;
|
||||
}
|
||||
@@ -19658,6 +19879,9 @@ f_type(typval_T *argvars, typval_T *rettv)
|
||||
else
|
||||
n = 7;
|
||||
break;
|
||||
#ifdef FEAT_JOB
|
||||
case VAR_JOB: n = 8; break;
|
||||
#endif
|
||||
case VAR_UNKNOWN:
|
||||
EMSG2(_(e_intern2), "f_type(UNKNOWN)");
|
||||
n = -1;
|
||||
@@ -21024,10 +21248,13 @@ free_tv(typval_T *varp)
|
||||
case VAR_DICT:
|
||||
dict_unref(varp->vval.v_dict);
|
||||
break;
|
||||
case VAR_NUMBER:
|
||||
#ifdef FEAT_FLOAT
|
||||
case VAR_FLOAT:
|
||||
case VAR_JOB:
|
||||
#ifdef FEAT_JOB
|
||||
job_unref(varp->vval.v_job);
|
||||
break;
|
||||
#endif
|
||||
case VAR_NUMBER:
|
||||
case VAR_FLOAT:
|
||||
case VAR_UNKNOWN:
|
||||
case VAR_SPECIAL:
|
||||
break;
|
||||
@@ -21065,11 +21292,17 @@ clear_tv(typval_T *varp)
|
||||
case VAR_SPECIAL:
|
||||
varp->vval.v_number = 0;
|
||||
break;
|
||||
#ifdef FEAT_FLOAT
|
||||
case VAR_FLOAT:
|
||||
#ifdef FEAT_FLOAT
|
||||
varp->vval.v_float = 0.0;
|
||||
break;
|
||||
#endif
|
||||
case VAR_JOB:
|
||||
#ifdef FEAT_JOB
|
||||
job_unref(varp->vval.v_job);
|
||||
varp->vval.v_job = NULL;
|
||||
#endif
|
||||
break;
|
||||
case VAR_UNKNOWN:
|
||||
break;
|
||||
}
|
||||
@@ -21112,8 +21345,8 @@ get_tv_number_chk(typval_T *varp, int *denote)
|
||||
{
|
||||
case VAR_NUMBER:
|
||||
return (long)(varp->vval.v_number);
|
||||
#ifdef FEAT_FLOAT
|
||||
case VAR_FLOAT:
|
||||
#ifdef FEAT_FLOAT
|
||||
EMSG(_("E805: Using a Float as a Number"));
|
||||
break;
|
||||
#endif
|
||||
@@ -21134,6 +21367,11 @@ get_tv_number_chk(typval_T *varp, int *denote)
|
||||
case VAR_SPECIAL:
|
||||
return varp->vval.v_number == VVAL_TRUE ? 1 : 0;
|
||||
break;
|
||||
case VAR_JOB:
|
||||
#ifdef FEAT_JOB
|
||||
EMSG(_("E910: Using a Job as a Number"));
|
||||
break;
|
||||
#endif
|
||||
case VAR_UNKNOWN:
|
||||
EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)");
|
||||
break;
|
||||
@@ -21153,10 +21391,8 @@ get_tv_float(typval_T *varp)
|
||||
{
|
||||
case VAR_NUMBER:
|
||||
return (float_T)(varp->vval.v_number);
|
||||
#ifdef FEAT_FLOAT
|
||||
case VAR_FLOAT:
|
||||
return varp->vval.v_float;
|
||||
#endif
|
||||
case VAR_FUNC:
|
||||
EMSG(_("E891: Using a Funcref as a Float"));
|
||||
break;
|
||||
@@ -21172,6 +21408,11 @@ get_tv_float(typval_T *varp)
|
||||
case VAR_SPECIAL:
|
||||
EMSG(_("E907: Using a special value as a Float"));
|
||||
break;
|
||||
case VAR_JOB:
|
||||
# ifdef FEAT_JOB
|
||||
EMSG(_("E911: Using a Job as a Float"));
|
||||
break;
|
||||
# endif
|
||||
case VAR_UNKNOWN:
|
||||
EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)");
|
||||
break;
|
||||
@@ -21272,8 +21513,8 @@ get_tv_string_buf_chk(typval_T *varp, char_u *buf)
|
||||
case VAR_DICT:
|
||||
EMSG(_("E731: using Dictionary as a String"));
|
||||
break;
|
||||
#ifdef FEAT_FLOAT
|
||||
case VAR_FLOAT:
|
||||
#ifdef FEAT_FLOAT
|
||||
EMSG(_(e_float_as_string));
|
||||
break;
|
||||
#endif
|
||||
@@ -21284,6 +21525,24 @@ get_tv_string_buf_chk(typval_T *varp, char_u *buf)
|
||||
case VAR_SPECIAL:
|
||||
STRCPY(buf, get_var_special_name(varp->vval.v_number));
|
||||
return buf;
|
||||
case VAR_JOB:
|
||||
#ifdef FEAT_JOB
|
||||
{
|
||||
job_T *job = varp->vval.v_job;
|
||||
char *status = job->jv_status == JOB_FAILED ? "fail"
|
||||
: job->jv_status == JOB_ENDED ? "dead"
|
||||
: "run";
|
||||
# ifdef UNIX
|
||||
vim_snprintf((char *)buf, NUMBUFLEN,
|
||||
"process %ld %s", (long)job->jv_pid, status);
|
||||
# else
|
||||
/* TODO */
|
||||
vim_snprintf((char *)buf, NUMBUFLEN, "process ? %s", status);
|
||||
# endif
|
||||
return buf;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case VAR_UNKNOWN:
|
||||
EMSG(_("E908: using an invalid value as a String"));
|
||||
break;
|
||||
@@ -21903,10 +22162,16 @@ copy_tv(typval_T *from, typval_T *to)
|
||||
case VAR_SPECIAL:
|
||||
to->vval.v_number = from->vval.v_number;
|
||||
break;
|
||||
#ifdef FEAT_FLOAT
|
||||
case VAR_FLOAT:
|
||||
#ifdef FEAT_FLOAT
|
||||
to->vval.v_float = from->vval.v_float;
|
||||
break;
|
||||
#endif
|
||||
case VAR_JOB:
|
||||
#ifdef FEAT_FLOAT
|
||||
to->vval.v_job = from->vval.v_job;
|
||||
++to->vval.v_job->jv_refcount;
|
||||
break;
|
||||
#endif
|
||||
case VAR_STRING:
|
||||
case VAR_FUNC:
|
||||
@@ -21970,12 +22235,11 @@ item_copy(
|
||||
switch (from->v_type)
|
||||
{
|
||||
case VAR_NUMBER:
|
||||
#ifdef FEAT_FLOAT
|
||||
case VAR_FLOAT:
|
||||
#endif
|
||||
case VAR_STRING:
|
||||
case VAR_FUNC:
|
||||
case VAR_SPECIAL:
|
||||
case VAR_JOB:
|
||||
copy_tv(from, to);
|
||||
break;
|
||||
case VAR_LIST:
|
||||
@@ -24649,6 +24913,7 @@ write_viminfo_varlist(FILE *fp)
|
||||
|
||||
case VAR_UNKNOWN:
|
||||
case VAR_FUNC:
|
||||
case VAR_JOB:
|
||||
continue;
|
||||
}
|
||||
fprintf(fp, "!%s\t%s\t", this_var->di_key, s);
|
||||
|
@@ -1255,12 +1255,19 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The Channel feature requires +eval.
|
||||
* The +channel feature requires +eval.
|
||||
*/
|
||||
#if !defined(FEAT_EVAL) && defined(FEAT_CHANNEL)
|
||||
# undef FEAT_CHANNEL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The +job feature requires Unix and +eval.
|
||||
*/
|
||||
#if defined(UNIX) && defined(FEAT_EVAL)
|
||||
# define FEAT_JOB
|
||||
#endif
|
||||
|
||||
/*
|
||||
* +signs Allow signs to be displayed to the left of text lines.
|
||||
* Adds the ":sign" command.
|
||||
|
202
src/os_unix.c
202
src/os_unix.c
@@ -3919,6 +3919,66 @@ wait4pid(pid_t child, waitstatus *status)
|
||||
return wait_pid;
|
||||
}
|
||||
|
||||
#if defined(FEAT_JOB) || !defined(USE_SYSTEM) || defined(PROTO)
|
||||
int
|
||||
mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc)
|
||||
{
|
||||
int i;
|
||||
char_u *p;
|
||||
int inquote;
|
||||
|
||||
/*
|
||||
* Do this loop twice:
|
||||
* 1: find number of arguments
|
||||
* 2: separate them and build argv[]
|
||||
*/
|
||||
for (i = 0; i < 2; ++i)
|
||||
{
|
||||
p = cmd;
|
||||
inquote = FALSE;
|
||||
*argc = 0;
|
||||
for (;;)
|
||||
{
|
||||
if (i == 1)
|
||||
(*argv)[*argc] = (char *)p;
|
||||
++*argc;
|
||||
while (*p != NUL && (inquote || (*p != ' ' && *p != TAB)))
|
||||
{
|
||||
if (*p == '"')
|
||||
inquote = !inquote;
|
||||
++p;
|
||||
}
|
||||
if (*p == NUL)
|
||||
break;
|
||||
if (i == 1)
|
||||
*p++ = NUL;
|
||||
p = skipwhite(p);
|
||||
}
|
||||
if (*argv == NULL)
|
||||
{
|
||||
if (use_shcf)
|
||||
{
|
||||
/* Account for possible multiple args in p_shcf. */
|
||||
p = p_shcf;
|
||||
for (;;)
|
||||
{
|
||||
p = skiptowhite(p);
|
||||
if (*p == NUL)
|
||||
break;
|
||||
++*argc;
|
||||
p = skipwhite(p);
|
||||
}
|
||||
}
|
||||
|
||||
*argv = (char **)alloc((unsigned)((*argc + 4) * sizeof(char *)));
|
||||
if (*argv == NULL) /* out of memory */
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
mch_call_shell(
|
||||
char_u *cmd,
|
||||
@@ -4046,7 +4106,7 @@ mch_call_shell(
|
||||
# define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use
|
||||
127, some shells use that already */
|
||||
|
||||
char_u *newcmd = NULL;
|
||||
char_u *newcmd;
|
||||
pid_t pid;
|
||||
pid_t wpid = 0;
|
||||
pid_t wait_pid = 0;
|
||||
@@ -4061,7 +4121,6 @@ mch_call_shell(
|
||||
char_u *p_shcf_copy = NULL;
|
||||
int i;
|
||||
char_u *p;
|
||||
int inquote;
|
||||
int pty_master_fd = -1; /* for pty's */
|
||||
# ifdef FEAT_GUI
|
||||
int pty_slave_fd = -1;
|
||||
@@ -4086,53 +4145,9 @@ mch_call_shell(
|
||||
if (options & SHELL_COOKED)
|
||||
settmode(TMODE_COOK); /* set to normal mode */
|
||||
|
||||
/*
|
||||
* Do this loop twice:
|
||||
* 1: find number of arguments
|
||||
* 2: separate them and build argv[]
|
||||
*/
|
||||
for (i = 0; i < 2; ++i)
|
||||
{
|
||||
p = newcmd;
|
||||
inquote = FALSE;
|
||||
argc = 0;
|
||||
for (;;)
|
||||
{
|
||||
if (i == 1)
|
||||
argv[argc] = (char *)p;
|
||||
++argc;
|
||||
while (*p && (inquote || (*p != ' ' && *p != TAB)))
|
||||
{
|
||||
if (*p == '"')
|
||||
inquote = !inquote;
|
||||
++p;
|
||||
}
|
||||
if (*p == NUL)
|
||||
break;
|
||||
if (i == 1)
|
||||
*p++ = NUL;
|
||||
p = skipwhite(p);
|
||||
}
|
||||
if (argv == NULL)
|
||||
{
|
||||
/*
|
||||
* Account for possible multiple args in p_shcf.
|
||||
*/
|
||||
p = p_shcf;
|
||||
for (;;)
|
||||
{
|
||||
p = skiptowhite(p);
|
||||
if (*p == NUL)
|
||||
break;
|
||||
++argc;
|
||||
p = skipwhite(p);
|
||||
}
|
||||
if (mch_parse_cmd(newcmd, TRUE, &argv, &argc) == FAIL)
|
||||
goto error;
|
||||
|
||||
argv = (char **)alloc((unsigned)((argc + 4) * sizeof(char *)));
|
||||
if (argv == NULL) /* out of memory */
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (cmd != NULL)
|
||||
{
|
||||
char_u *s;
|
||||
@@ -5006,6 +5021,97 @@ error:
|
||||
#endif /* USE_SYSTEM */
|
||||
}
|
||||
|
||||
#if defined(FEAT_JOB) || defined(PROTO)
|
||||
void
|
||||
mch_start_job(char **argv, job_T *job)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == -1) /* maybe we should use vfork() */
|
||||
{
|
||||
job->jv_status = JOB_FAILED;
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
/* child */
|
||||
reset_signals(); /* handle signals normally */
|
||||
|
||||
# ifdef HAVE_SETSID
|
||||
/* Create our own process group, so that the child and all its
|
||||
* children can be kill()ed. Don't do this when using pipes,
|
||||
* because stdin is not a tty, we would lose /dev/tty. */
|
||||
(void)setsid();
|
||||
# endif
|
||||
|
||||
/* See above for type of argv. */
|
||||
execvp(argv[0], argv);
|
||||
|
||||
perror("executing job failed");
|
||||
_exit(EXEC_FAILED); /* exec failed, return failure code */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* parent */
|
||||
job->jv_pid = pid;
|
||||
job->jv_status = JOB_STARTED;
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
mch_job_status(job_T *job)
|
||||
{
|
||||
# ifdef HAVE_UNION_WAIT
|
||||
union wait status;
|
||||
# else
|
||||
int status = -1;
|
||||
# endif
|
||||
pid_t wait_pid = 0;
|
||||
|
||||
# ifdef __NeXT__
|
||||
wait_pid = wait4(job->jv_pid, &status, WNOHANG, (struct rusage *)0);
|
||||
# else
|
||||
wait_pid = waitpid(job->jv_pid, &status, WNOHANG);
|
||||
# endif
|
||||
if (wait_pid == -1)
|
||||
{
|
||||
/* process must have exited */
|
||||
job->jv_status = JOB_ENDED;
|
||||
return "dead";
|
||||
}
|
||||
if (wait_pid == 0)
|
||||
return "run";
|
||||
if (WIFEXITED(status))
|
||||
{
|
||||
/* LINTED avoid "bitwise operation on signed value" */
|
||||
job->jv_exitval = WEXITSTATUS(status);
|
||||
job->jv_status = JOB_ENDED;
|
||||
return "dead";
|
||||
}
|
||||
return "run";
|
||||
}
|
||||
|
||||
int
|
||||
mch_stop_job(job_T *job, char_u *how)
|
||||
{
|
||||
int sig = -1;
|
||||
|
||||
if (STRCMP(how, "hup") == 0)
|
||||
sig = SIGHUP;
|
||||
else if (*how == NUL || STRCMP(how, "term") == 0)
|
||||
sig = SIGTERM;
|
||||
else if (STRCMP(how, "quit") == 0)
|
||||
sig = SIGQUIT;
|
||||
else if (STRCMP(how, "kill") == 0)
|
||||
sig = SIGKILL;
|
||||
else if (isdigit(*how))
|
||||
sig = atoi((char *)how);
|
||||
else
|
||||
return FAIL;
|
||||
kill(job->jv_pid, sig);
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check for CTRL-C typed by reading all available characters.
|
||||
* In cooked mode we should get SIGINT, no need to check.
|
||||
|
@@ -55,7 +55,11 @@ int mch_screenmode(char_u *arg);
|
||||
int mch_get_shellsize(void);
|
||||
void mch_set_shellsize(void);
|
||||
void mch_new_shellsize(void);
|
||||
int mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc);
|
||||
int mch_call_shell(char_u *cmd, int options);
|
||||
void mch_start_job(char **argv, job_T *job);
|
||||
char *mch_job_status(job_T *job);
|
||||
int mch_stop_job(job_T *job, char_u *how);
|
||||
void mch_breakcheck(void);
|
||||
int mch_expandpath(garray_T *gap, char_u *path, int flags);
|
||||
int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags);
|
||||
|
@@ -1110,17 +1110,19 @@ typedef double float_T;
|
||||
|
||||
typedef struct listvar_S list_T;
|
||||
typedef struct dictvar_S dict_T;
|
||||
typedef struct jobvar_S job_T;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VAR_UNKNOWN = 0,
|
||||
VAR_NUMBER, /* "v_number" is used */
|
||||
VAR_STRING, /* "v_string" is used */
|
||||
VAR_FUNC, /* "v_string" is function name */
|
||||
VAR_LIST, /* "v_list" is used */
|
||||
VAR_DICT, /* "v_dict" is used */
|
||||
VAR_FLOAT, /* "v_float" is used */
|
||||
VAR_SPECIAL /* "v_number" is used */
|
||||
VAR_NUMBER, /* "v_number" is used */
|
||||
VAR_STRING, /* "v_string" is used */
|
||||
VAR_FUNC, /* "v_string" is function name */
|
||||
VAR_LIST, /* "v_list" is used */
|
||||
VAR_DICT, /* "v_dict" is used */
|
||||
VAR_FLOAT, /* "v_float" is used */
|
||||
VAR_SPECIAL, /* "v_number" is used */
|
||||
VAR_JOB /* "v_job" is used */
|
||||
} vartype_T;
|
||||
|
||||
/*
|
||||
@@ -1139,6 +1141,9 @@ typedef struct
|
||||
char_u *v_string; /* string value (can be NULL!) */
|
||||
list_T *v_list; /* list value (can be NULL!) */
|
||||
dict_T *v_dict; /* dict value (can be NULL!) */
|
||||
#ifdef FEAT_JOB
|
||||
job_T *v_job; /* job value (can be NULL!) */
|
||||
#endif
|
||||
} vval;
|
||||
} typval_T;
|
||||
|
||||
@@ -1204,7 +1209,6 @@ struct dictitem_S
|
||||
char_u di_flags; /* flags (only used for variable) */
|
||||
char_u di_key[1]; /* key (actually longer!) */
|
||||
};
|
||||
|
||||
typedef struct dictitem_S dictitem_T;
|
||||
|
||||
#define DI_FLAGS_RO 1 /* "di_flags" value: read-only variable */
|
||||
@@ -1228,6 +1232,30 @@ struct dictvar_S
|
||||
dict_T *dv_used_prev; /* previous dict in used dicts list */
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
JOB_FAILED,
|
||||
JOB_STARTED,
|
||||
JOB_ENDED
|
||||
} jobstatus_T;
|
||||
|
||||
/*
|
||||
* Structure to hold info about a Job.
|
||||
*/
|
||||
struct jobvar_S
|
||||
{
|
||||
#ifdef UNIX
|
||||
pid_t jv_pid;
|
||||
int jv_exitval;
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
PROCESS_INFORMATION jf_pi;
|
||||
#endif
|
||||
jobstatus_T jv_status;
|
||||
|
||||
int jv_refcount; /* reference count */
|
||||
};
|
||||
|
||||
/* structure used for explicit stack while garbage collecting hash tables */
|
||||
typedef struct ht_stack_S
|
||||
{
|
||||
|
@@ -8,8 +8,9 @@ endif
|
||||
" This test requires the Python command to run the test server.
|
||||
" This most likely only works on Unix and Windows.
|
||||
if has('unix')
|
||||
" We also need the pkill command to make sure the server can be stopped.
|
||||
if !executable('python') || !executable('pkill')
|
||||
" We also need the job feature or the pkill command to make sure the server
|
||||
" can be stopped.
|
||||
if !(executable('python') && (has('job') || executable('pkill')))
|
||||
finish
|
||||
endif
|
||||
elseif has('win32')
|
||||
@@ -27,7 +28,9 @@ func s:start_server()
|
||||
" The Python program writes the port number in Xportnr.
|
||||
call delete("Xportnr")
|
||||
|
||||
if has('win32')
|
||||
if has('job')
|
||||
let s:job = job_start("python test_channel.py")
|
||||
elseif has('win32')
|
||||
silent !start cmd /c start "test_channel" py test_channel.py
|
||||
else
|
||||
silent !python test_channel.py&
|
||||
@@ -62,7 +65,9 @@ func s:start_server()
|
||||
endfunc
|
||||
|
||||
func s:kill_server()
|
||||
if has('win32')
|
||||
if has('job')
|
||||
call job_stop(s:job)
|
||||
elseif has('win32')
|
||||
call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"')
|
||||
else
|
||||
call system("pkill -f test_channel.py")
|
||||
|
@@ -289,6 +289,11 @@ static char *(features[]) =
|
||||
#else
|
||||
"-insert_expand",
|
||||
#endif
|
||||
#ifdef FEAT_JOB
|
||||
"+job",
|
||||
#else
|
||||
"-job",
|
||||
#endif
|
||||
#ifdef FEAT_JUMPLIST
|
||||
"+jumplist",
|
||||
#else
|
||||
@@ -742,6 +747,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1274,
|
||||
/**/
|
||||
1273,
|
||||
/**/
|
||||
|
Reference in New Issue
Block a user