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
|
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.
|
be running already. A negative number waits forever.
|
||||||
|
|
||||||
"timeout" is the time to wait for a request when blocking, using
|
"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,
|
When "mode" is "json" the "msg" argument is the body of the received message,
|
||||||
converted to Vim types.
|
converted to Vim types.
|
||||||
@ -104,13 +104,13 @@ possible to receive a message after sending one.
|
|||||||
|
|
||||||
The handler can be added or changed later: >
|
The handler can be added or changed later: >
|
||||||
call ch_setcallback(handle, {callback})
|
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
|
NOT IMPLEMENTED YET
|
||||||
|
|
||||||
The timeout can be changed later: >
|
The timeout can be changed later: >
|
||||||
call ch_settimeout(handle, {msec})
|
call ch_settimeout(handle, {msec})
|
||||||
NOT IMPLEMENTED YET
|
NOT IMPLEMENTED YET
|
||||||
|
*E906*
|
||||||
Once done with the channel, disconnect it like this: >
|
Once done with the channel, disconnect it like this: >
|
||||||
call ch_close(handle)
|
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
|
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 ~
|
1.1 Variable types ~
|
||||||
*E712*
|
*E712*
|
||||||
There are six types of variables:
|
There are eight types of variables:
|
||||||
|
|
||||||
Number A 32 or 64 bit signed number. |expr-number| *Number*
|
Number A 32 or 64 bit signed number. |expr-number| *Number*
|
||||||
Examples: -123 0x10 0177
|
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).
|
String A NUL terminated string of 8-bit unsigned characters (bytes).
|
||||||
|expr-string| Examples: "ab\txx\"--" 'x-z''a,c'
|
|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|.
|
List An ordered sequence of items |List|.
|
||||||
Example: [1, 2, ['a', 'b']]
|
Example: [1, 2, ['a', 'b']]
|
||||||
|
|
||||||
@ -59,6 +56,13 @@ Dictionary An associative, unordered array: Each entry has a key and a
|
|||||||
value. |Dictionary|
|
value. |Dictionary|
|
||||||
Example: {'blue': "#0000ff", 'red': "#ff0000"}
|
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
|
The Number and String types are converted automatically, depending on how they
|
||||||
are used.
|
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,
|
"foo" is converted to 0, which means FALSE. To test for a non-empty string,
|
||||||
use empty(): >
|
use empty(): >
|
||||||
:if !empty("foo")
|
: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*
|
*E805* *E806* *E808*
|
||||||
When mixing Number and Float the Number is converted to Float. Otherwise
|
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
|
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.
|
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.
|
When expecting a Float a Number can also be used, but nothing else.
|
||||||
|
|
||||||
*E706* *sticky-type-checking*
|
*E706* *sticky-type-checking*
|
||||||
@ -864,7 +869,7 @@ These three can be repeated and mixed. Examples:
|
|||||||
expr8 *expr8*
|
expr8 *expr8*
|
||||||
-----
|
-----
|
||||||
expr8[expr1] item of String or |List| *expr-[]* *E111*
|
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
|
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
|
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
|
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
|
isdirectory( {directory}) Number TRUE if {directory} is a directory
|
||||||
islocked( {expr}) Number TRUE if {expr} is locked
|
islocked( {expr}) Number TRUE if {expr} is locked
|
||||||
items( {dict}) List key-value pairs in {dict}
|
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
|
join( {list} [, {sep}]) String join {list} items into one String
|
||||||
jsondecode( {string}) any decode JSON
|
jsondecode( {string}) any decode JSON
|
||||||
jsonencode( {expr}) String encode JSON
|
jsonencode( {expr}) String encode JSON
|
||||||
@ -2668,6 +2676,7 @@ confirm({msg} [, {choices} [, {default} [, {type}]]])
|
|||||||
|
|
||||||
ch_close({handle}) *ch_close()*
|
ch_close({handle}) *ch_close()*
|
||||||
Close channel {handle}. See |channel|.
|
Close channel {handle}. See |channel|.
|
||||||
|
{only available when compiled with the |+channel| feature}
|
||||||
|
|
||||||
ch_open({address} [, {argdict}]) *ch_open()*
|
ch_open({address} [, {argdict}]) *ch_open()*
|
||||||
Open a channel to {address}. See |channel|.
|
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.,
|
{address} has the form "hostname:port", e.g.,
|
||||||
"localhost:8765".
|
"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:
|
items are:
|
||||||
mode "raw" or "json".
|
mode "raw" or "json".
|
||||||
Default "json".
|
Default "json".
|
||||||
@ -2686,10 +2695,11 @@ ch_open({address} [, {argdict}]) *ch_open()*
|
|||||||
Default: none.
|
Default: none.
|
||||||
waittime Specify connect timeout as milliseconds.
|
waittime Specify connect timeout as milliseconds.
|
||||||
Negative means forever.
|
Negative means forever.
|
||||||
Default: 0.
|
Default: 0 (don't wait)
|
||||||
timeout Specify response read timeout value as
|
timeout Specify response read timeout value as
|
||||||
milliseconds.
|
milliseconds.
|
||||||
Default: 2000.
|
Default: 2000.
|
||||||
|
{only available when compiled with the |+channel| feature}
|
||||||
|
|
||||||
ch_sendexpr({handle}, {expr} [, {callback}]) *ch_sendexpr()*
|
ch_sendexpr({handle}, {expr} [, {callback}]) *ch_sendexpr()*
|
||||||
Send {expr} over JSON channel {handle}. See |channel-use|.
|
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
|
function. It is called when the response is received. See
|
||||||
|channel-callback|.
|
|channel-callback|.
|
||||||
|
|
||||||
|
{only available when compiled with the |+channel| feature}
|
||||||
|
|
||||||
ch_sendraw({handle}, {string} [, {callback}]) *ch_sendraw()*
|
ch_sendraw({handle}, {string} [, {callback}]) *ch_sendraw()*
|
||||||
Send {string} over raw channel {handle}. See |channel-raw|.
|
Send {string} over raw channel {handle}. See |channel-raw|.
|
||||||
Works like |ch_sendexpr()|, but does not decode the response.
|
Works like |ch_sendexpr()|, but does not decode the response.
|
||||||
|
|
||||||
|
{only available when compiled with the |+channel| feature}
|
||||||
|
|
||||||
*copy()*
|
*copy()*
|
||||||
copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't
|
copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't
|
||||||
different from using {expr} directly.
|
different from using {expr} directly.
|
||||||
@ -2888,9 +2902,12 @@ diff_hlID({lnum}, {col}) *diff_hlID()*
|
|||||||
|
|
||||||
empty({expr}) *empty()*
|
empty({expr}) *empty()*
|
||||||
Return the Number 1 if {expr} is empty, zero otherwise.
|
Return the Number 1 if {expr} is empty, zero otherwise.
|
||||||
A |List| or |Dictionary| is empty when it does not have any
|
- A |List| or |Dictionary| is empty when it does not have any
|
||||||
items. A Number is empty when its value is zero.
|
items.
|
||||||
|v:false|, |v:none| and |v:null| are empty, |v:true| is not.
|
- 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
|
For a long |List| this is much faster than comparing the
|
||||||
length with zero.
|
length with zero.
|
||||||
|
|
||||||
@ -4286,6 +4303,73 @@ items({dict}) *items()*
|
|||||||
order.
|
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({list} [, {sep}]) *join()*
|
||||||
Join the items in {list} together into one String.
|
Join the items in {list} together into one String.
|
||||||
When {sep} is specified it is put in between the items. If
|
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
|
Float: 5
|
||||||
Boolean: 6 (v:false and v:true)
|
Boolean: 6 (v:false and v:true)
|
||||||
None 7 (v:null and v:none)
|
None 7 (v:null and v:none)
|
||||||
|
Job 8
|
||||||
To avoid the magic numbers it should be used this way: >
|
To avoid the magic numbers it should be used this way: >
|
||||||
:if type(myvar) == type(0)
|
:if type(myvar) == type(0)
|
||||||
:if type(myvar) == type("")
|
: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 long dict_len(dict_T *d);
|
||||||
static char_u *dict2string(typval_T *tv, int copyID);
|
static char_u *dict2string(typval_T *tv, int copyID);
|
||||||
static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate);
|
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 *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 *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
|
||||||
static char_u *string_quote(char_u *str, int function);
|
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_isdirectory(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_islocked(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);
|
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_join(typval_T *argvars, typval_T *rettv);
|
||||||
static void f_jsondecode(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);
|
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)
|
switch (tv1->v_type)
|
||||||
{
|
{
|
||||||
|
case VAR_UNKNOWN:
|
||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
case VAR_UNKNOWN:
|
case VAR_JOB:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VAR_LIST:
|
case VAR_LIST:
|
||||||
@ -3844,6 +3853,7 @@ item_lock(typval_T *tv, int deep, int lock)
|
|||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
|
case VAR_JOB:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VAR_LIST:
|
case VAR_LIST:
|
||||||
@ -5339,6 +5349,7 @@ eval_index(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
#endif
|
#endif
|
||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
|
case VAR_JOB:
|
||||||
if (verbose)
|
if (verbose)
|
||||||
EMSG(_("E909: Cannot index a special variable"));
|
EMSG(_("E909: Cannot index a special variable"));
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@ -5446,10 +5457,11 @@ eval_index(
|
|||||||
|
|
||||||
switch (rettv->v_type)
|
switch (rettv->v_type)
|
||||||
{
|
{
|
||||||
case VAR_SPECIAL:
|
case VAR_UNKNOWN:
|
||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
case VAR_UNKNOWN:
|
case VAR_SPECIAL:
|
||||||
|
case VAR_JOB:
|
||||||
break; /* not evaluating, skipping over subscript */
|
break; /* not evaluating, skipping over subscript */
|
||||||
|
|
||||||
case VAR_NUMBER:
|
case VAR_NUMBER:
|
||||||
@ -6167,9 +6179,6 @@ tv_equal(
|
|||||||
|
|
||||||
switch (tv1->v_type)
|
switch (tv1->v_type)
|
||||||
{
|
{
|
||||||
case VAR_UNKNOWN:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VAR_LIST:
|
case VAR_LIST:
|
||||||
++recursive_cnt;
|
++recursive_cnt;
|
||||||
r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE);
|
r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE);
|
||||||
@ -6190,11 +6199,6 @@ tv_equal(
|
|||||||
case VAR_NUMBER:
|
case VAR_NUMBER:
|
||||||
return tv1->vval.v_number == tv2->vval.v_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:
|
case VAR_STRING:
|
||||||
s1 = get_tv_string_buf(tv1, buf1);
|
s1 = get_tv_string_buf(tv1, buf1);
|
||||||
s2 = get_tv_string_buf(tv2, buf2);
|
s2 = get_tv_string_buf(tv2, buf2);
|
||||||
@ -6202,6 +6206,17 @@ tv_equal(
|
|||||||
|
|
||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
return tv1->vval.v_number == tv2->vval.v_number;
|
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
|
/* 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
|
static int
|
||||||
free_unref_items(int copyID)
|
free_unref_items(int copyID)
|
||||||
@ -6969,6 +6984,7 @@ free_unref_items(int copyID)
|
|||||||
}
|
}
|
||||||
ll = ll_next;
|
ll = ll_next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return did_free;
|
return did_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7694,6 +7710,40 @@ failret:
|
|||||||
return OK;
|
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 *
|
static char *
|
||||||
get_var_special_name(int nr)
|
get_var_special_name(int nr)
|
||||||
{
|
{
|
||||||
@ -7789,12 +7839,13 @@ echo_string(
|
|||||||
case VAR_STRING:
|
case VAR_STRING:
|
||||||
case VAR_NUMBER:
|
case VAR_NUMBER:
|
||||||
case VAR_UNKNOWN:
|
case VAR_UNKNOWN:
|
||||||
|
case VAR_JOB:
|
||||||
*tofree = NULL;
|
*tofree = NULL;
|
||||||
r = get_tv_string_buf(tv, numbuf);
|
r = get_tv_string_buf(tv, numbuf);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#ifdef FEAT_FLOAT
|
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
|
#ifdef FEAT_FLOAT
|
||||||
*tofree = NULL;
|
*tofree = NULL;
|
||||||
vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float);
|
vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float);
|
||||||
r = numbuf;
|
r = numbuf;
|
||||||
@ -7844,6 +7895,7 @@ tv2string(
|
|||||||
case VAR_LIST:
|
case VAR_LIST:
|
||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
|
case VAR_JOB:
|
||||||
case VAR_UNKNOWN:
|
case VAR_UNKNOWN:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -8148,6 +8200,11 @@ static struct fst
|
|||||||
{"isdirectory", 1, 1, f_isdirectory},
|
{"isdirectory", 1, 1, f_isdirectory},
|
||||||
{"islocked", 1, 1, f_islocked},
|
{"islocked", 1, 1, f_islocked},
|
||||||
{"items", 1, 1, f_items},
|
{"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},
|
{"join", 1, 2, f_join},
|
||||||
{"jsondecode", 1, 1, f_jsondecode},
|
{"jsondecode", 1, 1, f_jsondecode},
|
||||||
{"jsonencode", 1, 1, f_jsonencode},
|
{"jsonencode", 1, 1, f_jsonencode},
|
||||||
@ -10535,8 +10592,8 @@ f_empty(typval_T *argvars, typval_T *rettv)
|
|||||||
case VAR_NUMBER:
|
case VAR_NUMBER:
|
||||||
n = argvars[0].vval.v_number == 0;
|
n = argvars[0].vval.v_number == 0;
|
||||||
break;
|
break;
|
||||||
#ifdef FEAT_FLOAT
|
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
|
#ifdef FEAT_FLOAT
|
||||||
n = argvars[0].vval.v_float == 0.0;
|
n = argvars[0].vval.v_float == 0.0;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
@ -10552,6 +10609,11 @@ f_empty(typval_T *argvars, typval_T *rettv)
|
|||||||
n = argvars[0].vval.v_number != VVAL_TRUE;
|
n = argvars[0].vval.v_number != VVAL_TRUE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case VAR_JOB:
|
||||||
|
#ifdef FEAT_JOB
|
||||||
|
n = argvars[0].vval.v_job->jv_status != JOB_STARTED;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
case VAR_UNKNOWN:
|
case VAR_UNKNOWN:
|
||||||
EMSG2(_(e_intern2), "f_empty(UNKNOWN)");
|
EMSG2(_(e_intern2), "f_empty(UNKNOWN)");
|
||||||
n = TRUE;
|
n = TRUE;
|
||||||
@ -13060,6 +13122,9 @@ f_has(typval_T *argvars, typval_T *rettv)
|
|||||||
#ifdef FEAT_INS_EXPAND
|
#ifdef FEAT_INS_EXPAND
|
||||||
"insert_expand",
|
"insert_expand",
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef FEAT_JOB
|
||||||
|
"job",
|
||||||
|
#endif
|
||||||
#ifdef FEAT_JUMPLIST
|
#ifdef FEAT_JUMPLIST
|
||||||
"jumplist",
|
"jumplist",
|
||||||
#endif
|
#endif
|
||||||
@ -14188,6 +14253,161 @@ f_items(typval_T *argvars, typval_T *rettv)
|
|||||||
dict_list(argvars, rettv, 2);
|
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
|
* "join()" function
|
||||||
*/
|
*/
|
||||||
@ -14295,6 +14515,7 @@ f_len(typval_T *argvars, typval_T *rettv)
|
|||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
|
case VAR_JOB:
|
||||||
EMSG(_("E701: Invalid type for len()"));
|
EMSG(_("E701: Invalid type for len()"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -19658,6 +19879,9 @@ f_type(typval_T *argvars, typval_T *rettv)
|
|||||||
else
|
else
|
||||||
n = 7;
|
n = 7;
|
||||||
break;
|
break;
|
||||||
|
#ifdef FEAT_JOB
|
||||||
|
case VAR_JOB: n = 8; break;
|
||||||
|
#endif
|
||||||
case VAR_UNKNOWN:
|
case VAR_UNKNOWN:
|
||||||
EMSG2(_(e_intern2), "f_type(UNKNOWN)");
|
EMSG2(_(e_intern2), "f_type(UNKNOWN)");
|
||||||
n = -1;
|
n = -1;
|
||||||
@ -21024,10 +21248,13 @@ free_tv(typval_T *varp)
|
|||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
dict_unref(varp->vval.v_dict);
|
dict_unref(varp->vval.v_dict);
|
||||||
break;
|
break;
|
||||||
case VAR_NUMBER:
|
case VAR_JOB:
|
||||||
#ifdef FEAT_FLOAT
|
#ifdef FEAT_JOB
|
||||||
case VAR_FLOAT:
|
job_unref(varp->vval.v_job);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case VAR_NUMBER:
|
||||||
|
case VAR_FLOAT:
|
||||||
case VAR_UNKNOWN:
|
case VAR_UNKNOWN:
|
||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
break;
|
break;
|
||||||
@ -21065,11 +21292,17 @@ clear_tv(typval_T *varp)
|
|||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
varp->vval.v_number = 0;
|
varp->vval.v_number = 0;
|
||||||
break;
|
break;
|
||||||
#ifdef FEAT_FLOAT
|
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
|
#ifdef FEAT_FLOAT
|
||||||
varp->vval.v_float = 0.0;
|
varp->vval.v_float = 0.0;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case VAR_JOB:
|
||||||
|
#ifdef FEAT_JOB
|
||||||
|
job_unref(varp->vval.v_job);
|
||||||
|
varp->vval.v_job = NULL;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
case VAR_UNKNOWN:
|
case VAR_UNKNOWN:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -21112,8 +21345,8 @@ get_tv_number_chk(typval_T *varp, int *denote)
|
|||||||
{
|
{
|
||||||
case VAR_NUMBER:
|
case VAR_NUMBER:
|
||||||
return (long)(varp->vval.v_number);
|
return (long)(varp->vval.v_number);
|
||||||
#ifdef FEAT_FLOAT
|
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
|
#ifdef FEAT_FLOAT
|
||||||
EMSG(_("E805: Using a Float as a Number"));
|
EMSG(_("E805: Using a Float as a Number"));
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
@ -21134,6 +21367,11 @@ get_tv_number_chk(typval_T *varp, int *denote)
|
|||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
return varp->vval.v_number == VVAL_TRUE ? 1 : 0;
|
return varp->vval.v_number == VVAL_TRUE ? 1 : 0;
|
||||||
break;
|
break;
|
||||||
|
case VAR_JOB:
|
||||||
|
#ifdef FEAT_JOB
|
||||||
|
EMSG(_("E910: Using a Job as a Number"));
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
case VAR_UNKNOWN:
|
case VAR_UNKNOWN:
|
||||||
EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)");
|
EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)");
|
||||||
break;
|
break;
|
||||||
@ -21153,10 +21391,8 @@ get_tv_float(typval_T *varp)
|
|||||||
{
|
{
|
||||||
case VAR_NUMBER:
|
case VAR_NUMBER:
|
||||||
return (float_T)(varp->vval.v_number);
|
return (float_T)(varp->vval.v_number);
|
||||||
#ifdef FEAT_FLOAT
|
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
return varp->vval.v_float;
|
return varp->vval.v_float;
|
||||||
#endif
|
|
||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
EMSG(_("E891: Using a Funcref as a Float"));
|
EMSG(_("E891: Using a Funcref as a Float"));
|
||||||
break;
|
break;
|
||||||
@ -21172,6 +21408,11 @@ get_tv_float(typval_T *varp)
|
|||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
EMSG(_("E907: Using a special value as a Float"));
|
EMSG(_("E907: Using a special value as a Float"));
|
||||||
break;
|
break;
|
||||||
|
case VAR_JOB:
|
||||||
|
# ifdef FEAT_JOB
|
||||||
|
EMSG(_("E911: Using a Job as a Float"));
|
||||||
|
break;
|
||||||
|
# endif
|
||||||
case VAR_UNKNOWN:
|
case VAR_UNKNOWN:
|
||||||
EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)");
|
EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)");
|
||||||
break;
|
break;
|
||||||
@ -21272,8 +21513,8 @@ get_tv_string_buf_chk(typval_T *varp, char_u *buf)
|
|||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
EMSG(_("E731: using Dictionary as a String"));
|
EMSG(_("E731: using Dictionary as a String"));
|
||||||
break;
|
break;
|
||||||
#ifdef FEAT_FLOAT
|
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
|
#ifdef FEAT_FLOAT
|
||||||
EMSG(_(e_float_as_string));
|
EMSG(_(e_float_as_string));
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
@ -21284,6 +21525,24 @@ get_tv_string_buf_chk(typval_T *varp, char_u *buf)
|
|||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
STRCPY(buf, get_var_special_name(varp->vval.v_number));
|
STRCPY(buf, get_var_special_name(varp->vval.v_number));
|
||||||
return buf;
|
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:
|
case VAR_UNKNOWN:
|
||||||
EMSG(_("E908: using an invalid value as a String"));
|
EMSG(_("E908: using an invalid value as a String"));
|
||||||
break;
|
break;
|
||||||
@ -21903,10 +22162,16 @@ copy_tv(typval_T *from, typval_T *to)
|
|||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
to->vval.v_number = from->vval.v_number;
|
to->vval.v_number = from->vval.v_number;
|
||||||
break;
|
break;
|
||||||
#ifdef FEAT_FLOAT
|
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
|
#ifdef FEAT_FLOAT
|
||||||
to->vval.v_float = from->vval.v_float;
|
to->vval.v_float = from->vval.v_float;
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
case VAR_JOB:
|
||||||
|
#ifdef FEAT_FLOAT
|
||||||
|
to->vval.v_job = from->vval.v_job;
|
||||||
|
++to->vval.v_job->jv_refcount;
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
case VAR_STRING:
|
case VAR_STRING:
|
||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
@ -21970,12 +22235,11 @@ item_copy(
|
|||||||
switch (from->v_type)
|
switch (from->v_type)
|
||||||
{
|
{
|
||||||
case VAR_NUMBER:
|
case VAR_NUMBER:
|
||||||
#ifdef FEAT_FLOAT
|
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
#endif
|
|
||||||
case VAR_STRING:
|
case VAR_STRING:
|
||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
|
case VAR_JOB:
|
||||||
copy_tv(from, to);
|
copy_tv(from, to);
|
||||||
break;
|
break;
|
||||||
case VAR_LIST:
|
case VAR_LIST:
|
||||||
@ -24649,6 +24913,7 @@ write_viminfo_varlist(FILE *fp)
|
|||||||
|
|
||||||
case VAR_UNKNOWN:
|
case VAR_UNKNOWN:
|
||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
|
case VAR_JOB:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
fprintf(fp, "!%s\t%s\t", this_var->di_key, s);
|
fprintf(fp, "!%s\t%s\t", this_var->di_key, s);
|
||||||
|
|||||||
@ -1255,12 +1255,19 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The Channel feature requires +eval.
|
* The +channel feature requires +eval.
|
||||||
*/
|
*/
|
||||||
#if !defined(FEAT_EVAL) && defined(FEAT_CHANNEL)
|
#if !defined(FEAT_EVAL) && defined(FEAT_CHANNEL)
|
||||||
# undef FEAT_CHANNEL
|
# undef FEAT_CHANNEL
|
||||||
#endif
|
#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.
|
* +signs Allow signs to be displayed to the left of text lines.
|
||||||
* Adds the ":sign" command.
|
* 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;
|
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
|
int
|
||||||
mch_call_shell(
|
mch_call_shell(
|
||||||
char_u *cmd,
|
char_u *cmd,
|
||||||
@ -4046,7 +4106,7 @@ mch_call_shell(
|
|||||||
# define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use
|
# define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use
|
||||||
127, some shells use that already */
|
127, some shells use that already */
|
||||||
|
|
||||||
char_u *newcmd = NULL;
|
char_u *newcmd;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
pid_t wpid = 0;
|
pid_t wpid = 0;
|
||||||
pid_t wait_pid = 0;
|
pid_t wait_pid = 0;
|
||||||
@ -4061,7 +4121,6 @@ mch_call_shell(
|
|||||||
char_u *p_shcf_copy = NULL;
|
char_u *p_shcf_copy = NULL;
|
||||||
int i;
|
int i;
|
||||||
char_u *p;
|
char_u *p;
|
||||||
int inquote;
|
|
||||||
int pty_master_fd = -1; /* for pty's */
|
int pty_master_fd = -1; /* for pty's */
|
||||||
# ifdef FEAT_GUI
|
# ifdef FEAT_GUI
|
||||||
int pty_slave_fd = -1;
|
int pty_slave_fd = -1;
|
||||||
@ -4086,53 +4145,9 @@ mch_call_shell(
|
|||||||
if (options & SHELL_COOKED)
|
if (options & SHELL_COOKED)
|
||||||
settmode(TMODE_COOK); /* set to normal mode */
|
settmode(TMODE_COOK); /* set to normal mode */
|
||||||
|
|
||||||
/*
|
if (mch_parse_cmd(newcmd, TRUE, &argv, &argc) == FAIL)
|
||||||
* Do this loop twice:
|
goto error;
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
argv = (char **)alloc((unsigned)((argc + 4) * sizeof(char *)));
|
|
||||||
if (argv == NULL) /* out of memory */
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cmd != NULL)
|
if (cmd != NULL)
|
||||||
{
|
{
|
||||||
char_u *s;
|
char_u *s;
|
||||||
@ -5006,6 +5021,97 @@ error:
|
|||||||
#endif /* USE_SYSTEM */
|
#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.
|
* Check for CTRL-C typed by reading all available characters.
|
||||||
* In cooked mode we should get SIGINT, no need to check.
|
* 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);
|
int mch_get_shellsize(void);
|
||||||
void mch_set_shellsize(void);
|
void mch_set_shellsize(void);
|
||||||
void mch_new_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);
|
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);
|
void mch_breakcheck(void);
|
||||||
int mch_expandpath(garray_T *gap, char_u *path, int flags);
|
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);
|
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 listvar_S list_T;
|
||||||
typedef struct dictvar_S dict_T;
|
typedef struct dictvar_S dict_T;
|
||||||
|
typedef struct jobvar_S job_T;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
VAR_UNKNOWN = 0,
|
VAR_UNKNOWN = 0,
|
||||||
VAR_NUMBER, /* "v_number" is used */
|
VAR_NUMBER, /* "v_number" is used */
|
||||||
VAR_STRING, /* "v_string" is used */
|
VAR_STRING, /* "v_string" is used */
|
||||||
VAR_FUNC, /* "v_string" is function name */
|
VAR_FUNC, /* "v_string" is function name */
|
||||||
VAR_LIST, /* "v_list" is used */
|
VAR_LIST, /* "v_list" is used */
|
||||||
VAR_DICT, /* "v_dict" is used */
|
VAR_DICT, /* "v_dict" is used */
|
||||||
VAR_FLOAT, /* "v_float" is used */
|
VAR_FLOAT, /* "v_float" is used */
|
||||||
VAR_SPECIAL /* "v_number" is used */
|
VAR_SPECIAL, /* "v_number" is used */
|
||||||
|
VAR_JOB /* "v_job" is used */
|
||||||
} vartype_T;
|
} vartype_T;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1139,6 +1141,9 @@ typedef struct
|
|||||||
char_u *v_string; /* string value (can be NULL!) */
|
char_u *v_string; /* string value (can be NULL!) */
|
||||||
list_T *v_list; /* list value (can be NULL!) */
|
list_T *v_list; /* list value (can be NULL!) */
|
||||||
dict_T *v_dict; /* dict 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;
|
} vval;
|
||||||
} typval_T;
|
} typval_T;
|
||||||
|
|
||||||
@ -1204,7 +1209,6 @@ struct dictitem_S
|
|||||||
char_u di_flags; /* flags (only used for variable) */
|
char_u di_flags; /* flags (only used for variable) */
|
||||||
char_u di_key[1]; /* key (actually longer!) */
|
char_u di_key[1]; /* key (actually longer!) */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct dictitem_S dictitem_T;
|
typedef struct dictitem_S dictitem_T;
|
||||||
|
|
||||||
#define DI_FLAGS_RO 1 /* "di_flags" value: read-only variable */
|
#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 */
|
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 */
|
/* structure used for explicit stack while garbage collecting hash tables */
|
||||||
typedef struct ht_stack_S
|
typedef struct ht_stack_S
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,8 +8,9 @@ endif
|
|||||||
" This test requires the Python command to run the test server.
|
" This test requires the Python command to run the test server.
|
||||||
" This most likely only works on Unix and Windows.
|
" This most likely only works on Unix and Windows.
|
||||||
if has('unix')
|
if has('unix')
|
||||||
" We also need the pkill command to make sure the server can be stopped.
|
" We also need the job feature or the pkill command to make sure the server
|
||||||
if !executable('python') || !executable('pkill')
|
" can be stopped.
|
||||||
|
if !(executable('python') && (has('job') || executable('pkill')))
|
||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
elseif has('win32')
|
elseif has('win32')
|
||||||
@ -27,7 +28,9 @@ func s:start_server()
|
|||||||
" The Python program writes the port number in Xportnr.
|
" The Python program writes the port number in Xportnr.
|
||||||
call delete("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
|
silent !start cmd /c start "test_channel" py test_channel.py
|
||||||
else
|
else
|
||||||
silent !python test_channel.py&
|
silent !python test_channel.py&
|
||||||
@ -62,7 +65,9 @@ func s:start_server()
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func s:kill_server()
|
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"')
|
call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"')
|
||||||
else
|
else
|
||||||
call system("pkill -f test_channel.py")
|
call system("pkill -f test_channel.py")
|
||||||
|
|||||||
@ -289,6 +289,11 @@ static char *(features[]) =
|
|||||||
#else
|
#else
|
||||||
"-insert_expand",
|
"-insert_expand",
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef FEAT_JOB
|
||||||
|
"+job",
|
||||||
|
#else
|
||||||
|
"-job",
|
||||||
|
#endif
|
||||||
#ifdef FEAT_JUMPLIST
|
#ifdef FEAT_JUMPLIST
|
||||||
"+jumplist",
|
"+jumplist",
|
||||||
#else
|
#else
|
||||||
@ -742,6 +747,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 */
|
||||||
|
/**/
|
||||||
|
1274,
|
||||||
/**/
|
/**/
|
||||||
1273,
|
1273,
|
||||||
/**/
|
/**/
|
||||||
|
|||||||
Reference in New Issue
Block a user